/*
 * Copyright 2008-2012 Centro Andaluz de Innovación y Tecnologías de la
 * Información y las Comunicaciones (CITIC). All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Please contact CITIC - Edificio CITIC, C/ Marie Curie, 6 Parque
 * Tecnológico de Andalucía 29590 - Campanillas, Málaga, Spain
 * or visit www.citic.es, if you need additional information or
 * have any questions.
 */

package es.citic.puedes.youtube.impl;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import javax.persistence.EntityManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gdata.client.Query;
import com.google.gdata.client.youtube.YouTubeQuery;
import com.google.gdata.client.youtube.YouTubeQuery.SafeSearch;
import com.google.gdata.client.youtube.YouTubeService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Link;
import com.google.gdata.data.Person;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.extensions.Rating;
import com.google.gdata.data.media.MediaFileSource;
import com.google.gdata.data.media.mediarss.MediaCategory;
import com.google.gdata.data.media.mediarss.MediaDescription;
import com.google.gdata.data.media.mediarss.MediaKeywords;
import com.google.gdata.data.media.mediarss.MediaTitle;
import com.google.gdata.data.youtube.CommentEntry;
import com.google.gdata.data.youtube.CommentFeed;
import com.google.gdata.data.youtube.FriendEntry;
import com.google.gdata.data.youtube.FriendFeed;
import com.google.gdata.data.youtube.PlaylistEntry;
import com.google.gdata.data.youtube.PlaylistFeed;
import com.google.gdata.data.youtube.PlaylistLinkEntry;
import com.google.gdata.data.youtube.PlaylistLinkFeed;
import com.google.gdata.data.youtube.RatingEntry;
import com.google.gdata.data.youtube.UserProfileEntry;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.VideoFeed;
import com.google.gdata.data.youtube.YouTubeMediaGroup;
import com.google.gdata.data.youtube.YouTubeNamespace;
import com.google.gdata.data.youtube.YtGender;
import com.google.gdata.data.youtube.YtRelationship;
import com.google.gdata.data.youtube.YtStatus;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

import es.citic.puedes.youtube.IYouTubeManager;
import es.citic.puedes.youtube.IYouTubeUserModel;
import es.citic.puedes.youtube.Preference;
import es.citic.puedes.youtube.YouTubeAuthException;
import es.citic.puedes.youtube.YouTubeException;
import es.citic.puedes.youtube.bbdd.YouTubeDataBase;
import es.citic.puedes.youtube.model.Category;
import es.citic.puedes.youtube.model.OrderBy;
import es.citic.puedes.youtube.model.StandarVideos;
import es.citic.puedes.youtube.model.UserPreferences;
import es.citic.puedes.youtube.model.YouTubeComment;
import es.citic.puedes.youtube.model.YouTubeContact;
import es.citic.puedes.youtube.model.YouTubeContact.Status;
import es.citic.puedes.youtube.model.YouTubePlayList;
import es.citic.puedes.youtube.model.YouTubePlayListEntry;
import es.citic.puedes.youtube.model.YouTubePreferences;
import es.citic.puedes.youtube.model.YouTubeResult;
import es.citic.puedes.youtube.model.YouTubeType;
import es.citic.puedes.youtube.model.YouTubeUser;
import es.citic.puedes.youtube.model.YouTubeVideo;
import es.citic.puedes.youtube.model.YouTubeVideo.State;
import es.citic.puedes.youtube.model.YouTubeVideoMinimal;
import es.citic.puedes.youtube.tables.Userkeyword;
import es.citic.puedes.youtube.tables.YoutubeUser;
import es.citic.puedes.youtube.umm.impl.YoutubeUserModelImpl;

/**
 * class that implements the YouTubeManager interface. Provides the complete
 * functionality of the YouTube service 
 */

public class YouTubeManagerImpl implements IYouTubeManager {

	/**
	 * The name of the server hosting the YouTube feeds.
	 */
	public String YOUTUBE_SERVER = "";// "http://gdata.youtube.com";

	/**
	 * The URL of the standard video feeds
	 */
	public String STANDARD_FEED = "";// YOUTUBE_SERVER
	// + "/feeds/api/standardfeeds/";

	/**
	 * The URL of the video feeds
	 */
	public String VIDEOS_FEED = "";// YOUTUBE_SERVER
	// + "/feeds/api/videos/";

	/**
	 * The prefix of the user feeds
	 */
	public String USER_FEED_PREFIX = "";// YOUTUBE_SERVER
	// + "/feeds/api/users/";

	/**
	 * The prefix of the playlist feeds
	 */
	public String PLAYLIST_FEED_PREFIX = "";// YOUTUBE_SERVER
	// + "/feeds/api/playlists/";

	/**
	 * YouTube service name
	 */
	private static final String SERVICENAME = "YouTubeManager";

	/**
	 * developer key for YouTube service
	 */
	public String DEVKEY = "";// "AI39si5sRgjTtzE4g9jv_VMl1NNqfxuhZETK0RkNdfesBeLNoXJzNd1yk0fDK8QMyY9ijZmSAn56Y3oH2vYB2HOyXbmpyq04SQ";

	/**
	 * Java client service for the YouTube API
	 */
	private YouTubeService service;

	/**
	 * DBServiceManager service interface
	 */
	private YouTubeDataBase youTubeDB;

	/**
	 * Log instance
	 */
	private static Logger logger = LoggerFactory.getLogger(YouTubeManagerImpl.class);

	public YouTubeManagerImpl(String configFile) {
		this(configFile,new YouTubeDataBase(new DataConfigManager(configFile).getValueFromConfigXml("youtubemanager","config", "persistence_unit")));		
	}


	public YouTubeManagerImpl(String configFile,YouTubeDataBase youtubeDataBase) {		
		logger.debug("YouTubeManagerImpl..");
		// sets the category to false to initialize the category state
		// deprecatedCategory = false;
		service = null;

		DataConfigManager dcm = new DataConfigManager(configFile);

		// initializes service configuration
		DEVKEY = dcm.getValueFromConfigXml("youtubemanager", "config", "key");
		YOUTUBE_SERVER = dcm.getValueFromConfigXml("youtubemanager", "config", "domain");
		STANDARD_FEED = YOUTUBE_SERVER + dcm.getValueFromConfigXml("youtubemanager", "config", "standard_feed");
		VIDEOS_FEED = YOUTUBE_SERVER + dcm.getValueFromConfigXml("youtubemanager","config", "videos_feed");
		USER_FEED_PREFIX = YOUTUBE_SERVER + dcm.getValueFromConfigXml("youtubemanager","config", "user_feed");
		PLAYLIST_FEED_PREFIX = YOUTUBE_SERVER+ dcm.getValueFromConfigXml("youtubemanager","config", "playlist_feed");

		//youTubeDB = new YouTubeDataBase(dcm.getValueFromConfigXml("youtubemanager", "config", "persistence_unit"));
		youTubeDB = youtubeDataBase;

		// creates the instance of the YouTube service
		service = new YouTubeService(SERVICENAME, DEVKEY);
	}

	/**
	 * 	
	 * @throws Throwable
	 */
	public void install() throws Throwable {
		logger.debug("Begins YouTube service installation..");

		youTubeDB.install();

		logger.debug("YouTube service installation..");
	}


	@Override
	public void registreUser(Integer userId, String userName, String password) throws YouTubeException, YouTubeAuthException {
		logger.debug("registerUser..");

		if ((userId == null ) || (userName == null) || (password == null)){
			throw new YouTubeException("Parameters can't be null");
		}

		// checks whether the user was already stored
		YoutubeUser youtubeUserStored = getYoutubeUser(userId);

		logger.debug("registreUser after checkYouTubeUser..");

		EntityManager em = youTubeDB.getEntityManager();

		if (youtubeUserStored != null){// if the same userId was already stored..
			youTubeDB.closeEntityManager(em);
			throw new YouTubeException("An user with that id was already stored");

		} else if (youTubeDB.selectYouTubeUserNameQuery(em, userName)) {// if the same userName was already stored..
			youTubeDB.closeEntityManager(em);
			throw new YouTubeException("Another user exists for this username in the BBDD");

		} else {// if the id and username doesn't exists	
			resetYouTubeService();

			// sets the credentials of the new YouTube user
			try {
				checkUserCredentials(userName, password);
				em.getTransaction().begin();
				YoutubeUser newYoutubeUser = new YoutubeUser(password,	userId, userName);
				em.persist(newYoutubeUser);
				em.getTransaction().commit();
				youTubeDB.closeEntityManager(em);

			} catch (YouTubeAuthException e) {
				youTubeDB.closeEntityManager(em);
				throw new YouTubeAuthException("Register fails. "+ e.getMessage()+ ",you need a correct username or password", e.getCause());
			} catch (Exception e) {
				em.getTransaction().rollback();
				youTubeDB.closeEntityManager(em);
				throw new YouTubeException("the user could not be registered: "	+ e.getMessage());
			}

			logger.debug(" register ends");
		} 
	}

	@Override
	public YouTubeResult search(YouTubeType type, OrderBy order, List<String> keywords) throws YouTubeException {
		logger.debug("search..");

		// sets the anonymous user
		resetYouTubeService();

		YouTubeResult modelSearchResult = new YouTubeResult();

		if (type.getReadableNameType().equals("Videos")) {

			// searches videos and adds them to the YouTubeResult object
			List<YouTubeVideoMinimal> foundVideoList = null;
			foundVideoList = searchVideos(order, null, keywords);
			modelSearchResult.setVideos(foundVideoList);

		} else if (type.getReadableNameType().equals("Listas reproduccion")) {

			// searches playlists and adds them to the YouTubeResult object
			List<YouTubePlayList> foundPlayListList = null;
			foundPlayListList = searchPlayLists(keywords);
			modelSearchResult.setPlayLists(foundPlayListList);
			logger.debug("entra para buscar listas");

			// search all...
		} else {

			// searches videos and adds them to the YouTubeResult object
			List<YouTubeVideoMinimal> foundVideoList = null;
			foundVideoList = searchVideos(order, null, keywords);
			modelSearchResult.setVideos(foundVideoList);

			// searches playlists and adds them to the YouTubeResult object
			List<YouTubePlayList> foundPlayListList = null;
			foundPlayListList = searchPlayLists(keywords);
			modelSearchResult.setPlayLists(foundPlayListList);
		}

		return modelSearchResult;
	}

	@Override
	public List<YouTubeVideoMinimal> explore(Category category, OrderBy order, List<String> keywords) throws YouTubeException {
		logger.debug("explore..");

		// sets the anonymous user
		resetYouTubeService();

		List<YouTubeVideoMinimal> foundVideoList = null;

		if (category.isCategoryDeprecated()) {
			logger.debug("this category is deprecated");
		}

		// searches videos using a category
		foundVideoList = searchVideos(order, category, keywords);

		// sets the category to false to initialize the category state
		// setCategoryDeprecated(false);

		return foundVideoList;
	}

	@Override
	public YouTubeVideo getVideo(String youtubeVideoId) throws YouTubeException {

		logger.debug("getVideo..");

		// sets the anonymous user
		resetYouTubeService();

		YouTubeVideo modelVideo = new YouTubeVideo();

		// gets the video entry from the url
		VideoEntry videoEntry = getVideoEntryFromYoutubeVideoId(youtubeVideoId);

		// converts the video entry into a model video
		modelVideo = convertVideoEntryToModelVideo(videoEntry);

		return modelVideo;
	}

	@Override
	public void addRating(YouTubeUser user, String videoID, Integer value) throws YouTubeException, YouTubeAuthException {

		logger.debug("addRating..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the video id has the correct pattern
		if (!videoIdIsCorrect(videoID, "video")) {
			throw new YouTubeException("video id is not correct");
		}

		// url for getting the video
		String entryUrl = null;
		entryUrl = VIDEOS_FEED + videoID;

		VideoEntry videoEntryToRate = null;

		try {
			// gets the video entry
			videoEntryToRate = getYouTubeVideoEntry(entryUrl);

			// url for getting the ratings from the video entry
			String ratingUrl = videoEntryToRate.getRatingLink().getHref();

			// creates a Rating object and set the max and min values
			Rating myRating = new Rating();
			myRating.setMin(1);
			myRating.setMax(5);

			// only 4-5 put the video on my_liked_videos
			// if you set 1-3, remove the video from my_liked_videos
			myRating.setValue(value);

			// set the rating
			RatingEntry ratingEntry = new RatingEntry();
			ratingEntry.setRating(myRating);

			logger.debug("rating the video '" + videoID + "'...");
			// update the rating for the video entry
			service.insert(new URL(ratingUrl), ratingEntry);
			logger.debug("video rated");

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public void addComment(YouTubeUser user, String videoID, String comment) throws YouTubeException, YouTubeAuthException {

		logger.debug("addComment..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the video id has the correct pattern
		if (!videoIdIsCorrect(videoID, "video")) {
			throw new YouTubeException("video id is not correct");
		}

		// url for getting the video
		String entryUrl = null;
		entryUrl = (VIDEOS_FEED + videoID);

		VideoEntry videoEntryToAddComment = null;

		// gets the video entry to add a comment
		videoEntryToAddComment = getYouTubeVideoEntry(entryUrl);

		// url for getting the comments from the video entry
		String commentUrl = videoEntryToAddComment.getComments().getFeedLink().getHref();

		// creates the new comment using the parameter 'comment'
		CommentEntry newComment = new CommentEntry();
		newComment.setContent(new PlainTextConstruct(comment));
		try {
			// insert the new comment for the video entry
			logger.debug("adding comment to video '" + videoID + "'...");
			service.insert(new URL(commentUrl), newComment);
			logger.debug("Comment added successfully!\n");

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public void insertVideo(YouTubeUser user, YouTubeVideo video, String videoPath) throws YouTubeException, YouTubeAuthException {
		logger.debug("insertVideo..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if video is correctly initialized
		if (video.getPublicationState() == null) {
			throw new YouTubeException("video for uploading is empty");

		} else if (video.getCategories().get(0).isCategoryDeprecated()) {
			throw new YouTubeException("this category for uploading videos is deprecated");

		} else {
			File mediaFile = new File(videoPath);
			
			if (mediaFile.exists() == false) {
				throw new YouTubeException("File doesn't exists");
			}
			
			// gets the media type for the video
			String videoType = getVideoType(mediaFile.getPath());

			if (videoType == null) {
				throw new YouTubeException("invalid video type");			
			}
			
			MediaFileSource mediaSource = new MediaFileSource(mediaFile, videoType);	
			logger.debug("ContentFile:" + mediaSource.getMediaFile().getPath());	
			logger.debug("ContentType:" + mediaSource.getContentType());		
						
			// converts the model video into a VideoEntry video for uploading it
			VideoEntry videoToUpload = convertModelVideoToVideoEntry(video);	
			videoToUpload.setMediaSource(mediaSource);

			// url for uploading a video by the current authenticated user
			String uploadUrl = USER_FEED_PREFIX + "default/uploads";
			
			try {
				URL url = new URL(uploadUrl);
				
				logger.debug("uploading video...");
				// uploads the video and creates the new VideoEntry
				VideoEntry createdEntry = service.insert(new URL(uploadUrl), videoToUpload);
				logger.debug("video uploaded");
				logger.debug("url for the new video entry :"+ createdEntry.getHtmlLink().getHref());

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());

			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());

			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}
		}
	}

	@Override
	public void deleteVideo(YouTubeUser user, String videoID) throws YouTubeException, YouTubeAuthException {
		logger.debug("deleteVideo..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the video id has the correct pattern
		if (!videoIdIsCorrect(videoID, "video")) {
			throw new YouTubeException("video id is not correct");
		}

		// url for editing the video by the current authenticated user
		String videoEntryUrl = USER_FEED_PREFIX + "default/uploads/" + videoID;

		VideoEntry videoEntryToDelete = null;

		try {
			// gets the video entry to delete
			videoEntryToDelete = getYouTubeVideoEntry(videoEntryUrl);

			// deletes the video entry
			logger.debug("deleting video '" + videoID + "' ...");
			videoEntryToDelete.delete();
			logger.debug("video deleted");

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public List<YouTubeVideoMinimal> getUserVideos(YouTubeUser user) throws YouTubeException, YouTubeAuthException {
		logger.debug("getUserVideos..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubeVideoMinimal> modelUploadVideoList = null;

		// url for getting the upload videos by the current user
		String userVideoFeedUrl = USER_FEED_PREFIX + "default/uploads";

		VideoFeed userVideoFeed = null;

		// gets the video feed with the videos uploaded
		userVideoFeed = getYouTubeVideoFeed(userVideoFeedUrl);

		// gets the video list from the feed
		List<VideoEntry> videoEntryList = null;
		videoEntryList = getTotalVideoEntries(userVideoFeed);

		// transforms the video entry list into a model video minimal list
		modelUploadVideoList = convertVideoEntryListToModelVideoMinimalList(videoEntryList);

		return modelUploadVideoList;
	}

	@Override
	public List<YouTubeVideoMinimal> getFavorites(YouTubeUser user) throws YouTubeException, YouTubeAuthException {
		logger.debug("getFavorites..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubeVideoMinimal> modelFavoriteVideoList = null;

		// url for getting favorites from the current user
		String favoriteUrl = USER_FEED_PREFIX + "default/favorites";

		// gets the video feed with the favorite videos
		VideoFeed favoriteVideoFeed = null;
		favoriteVideoFeed = getYouTubeVideoFeed(favoriteUrl);

		// gets the favorite video list from the feed
		List<VideoEntry> favoriteVideoList = null;
		favoriteVideoList = getTotalVideoEntries(favoriteVideoFeed);

		// converts the list of video entries into a model YouTubeVideoMinimal object list
		modelFavoriteVideoList = convertVideoEntryListToModelVideoMinimalList(favoriteVideoList);

		return modelFavoriteVideoList;
	}

	@Override
	public void addFavorites(YouTubeUser user, String videoID) throws YouTubeException, YouTubeAuthException {
		logger.debug("addFavorites..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the video id has the correct pattern
		if (!videoIdIsCorrect(videoID, "video")) {
			throw new YouTubeException("video id is not correct");
		}

		// url for getting the video
		String entryUrl = VIDEOS_FEED + videoID;

		VideoEntry videoEntryToAddAsFavorite = null;
		try {
			// gets the video entry to add as favorite
			videoEntryToAddAsFavorite = getYouTubeVideoEntry(entryUrl);

			// url for editting the favorites from the current user
			String favoriteUrl = USER_FEED_PREFIX + "default/favorites";

			logger.debug("adding video '" + videoID + "' as favorite...");
			// insert the video into the favorites from the current user
			service.insert(new URL(favoriteUrl), videoEntryToAddAsFavorite);
			logger.debug("video added as favorite");

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public void deleteFavorites(YouTubeUser user, String favoriteID) throws YouTubeException, YouTubeAuthException {
		logger.debug("deleteFavorites..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the video id has the correct pattern
		if (!videoIdIsCorrect(favoriteID, "favorite")) {
			throw new YouTubeException("video id is not correct");
		}

		// url for getting the favorites
		String favoriteUrl = USER_FEED_PREFIX + "default/favorites";

		// url for getting a favorite video entry from the user
		String favoriteVideoUrl = favoriteUrl + "/" + favoriteID;

		// gets the favorite video entry to delete
		VideoEntry favoriteVideoToDelete = null;
		favoriteVideoToDelete = getYouTubeVideoEntry(favoriteVideoUrl);

		// if the favorite video exists...
		if (favoriteVideoToDelete != null) {

			try {
				// deletes the video entry from the favorites
				logger.debug("deleting video '" + favoriteID + "' from favorites...");
				favoriteVideoToDelete.delete();
				logger.debug("video deleted from favorites");

			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());

			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

		} else {

			logger.debug("favorite video to delete not found");
			// throw new YouTubeException("favorite video to delete not found");
		}

	}

	@Override
	public List<YouTubePlayList> getPlayLists(YouTubeUser user) throws YouTubeException, YouTubeAuthException {
		logger.debug("getPlayLists..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubePlayList> userListOfModelPlaylists = new ArrayList<YouTubePlayList>();

		// url for getting the playlists from the current user
		String playListFeedUrl = USER_FEED_PREFIX + "default/playlists";

		// gets the feed with the playlists
		PlaylistLinkFeed playlistLinkFeed = null;
		playlistLinkFeed = getYouTubePlaylistLinkFeed(playListFeedUrl);

		// gets the complete list with the playlists from the feed
		List<PlaylistLinkEntry> listOfPlayListLinkEntries = null;
		listOfPlayListLinkEntries = getTotalPlayListLinkEntries(playlistLinkFeed);

		// if the list is not empty...
		if (!listOfPlayListLinkEntries.isEmpty()) {

			// converts the YouTube list of playlists into a model list of
			// playlists
			userListOfModelPlaylists = convertListOfPlayListsToListOfModelPlayLists(listOfPlayListLinkEntries);

		}

		return userListOfModelPlaylists;
	}

	@Override
	public List<YouTubePlayListEntry> getPlayList(String playListId) throws YouTubeException {
		logger.debug("getPlayList..");

		// sets the anonymous user
		resetYouTubeService();

		List<YouTubePlayListEntry> modelPlayListEntries = new ArrayList<YouTubePlayListEntry>();

		if (playListId != null) {
			
			// url for getting the playlist
			String playlistUrl = PLAYLIST_FEED_PREFIX + playListId;

			// gets the playlist feed with the playlist entries (video entries)
			PlaylistFeed playlistFeed = getYouTubePlaylistFeed(playlistUrl);

			// gets the first hundred entries from the feed
			List<PlaylistEntry> playlistEntries = getTotalPlayListEntries(playlistFeed, 100);

			// converts the PlayListEntry object list into a model
			// YouTubePlayListEntry object list
			modelPlayListEntries = convertListOfPlayListEntryToListOfModelPlayListEntry(playlistEntries);

		} else {
			throw new YouTubeException("the playlist id is not correct ");
		}
		return modelPlayListEntries;
	}
	
	@Override
	public List<YouTubePlayListEntry> getPlayList(String playListId, Integer indexStart, Integer maxResults) throws YouTubeException {
		logger.debug("getPlayList..");

		// sets the anonymous user
		resetYouTubeService();

		List<YouTubePlayListEntry> modelPlayListEntries = new ArrayList<YouTubePlayListEntry>();

		if (playListId != null) {
			
			// url for getting the playlist
			String playlistUrl = PLAYLIST_FEED_PREFIX + playListId;

			// gets the playlist feed with the playlist entries (video entries)
			PlaylistFeed playlistFeed;
			// playlistFeed = getYouTubePlaylistFeed(playlistUrl);

			YouTubeQuery query = null;

			try {
				// constructs the YouTubeQuery object for getting the playlist
				// feed by paging
				query = new YouTubeQuery(new URL(playlistUrl));
			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// sets the offset and max number of elements to get
			query.setMaxResults(maxResults);
			query.setStartIndex(indexStart);

			// gets the playlist feed with the videos
			try {
				playlistFeed = service.query(query, PlaylistFeed.class);
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// gets the entries from the feed
			List<PlaylistEntry> playlistEntries = getTotalPlayListEntries(
					playlistFeed, maxResults);

			// converts the PlayListEntry object list into a model
			// YouTubePlayListEntry object list
			modelPlayListEntries = convertListOfPlayListEntryToListOfModelPlayListEntry(playlistEntries);

		} else {
			throw new YouTubeException("the playlist id is not correct ");
		}
		return modelPlayListEntries;
	}	

	@Override
	public String addPlayList(YouTubeUser user, String title, String description) throws YouTubeException, YouTubeAuthException {
		logger.debug("addPlayList..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		String createdPlayListId = null;

		// url for editing the playlists from the current user
		String playlistUrl = USER_FEED_PREFIX + "default/playlists";

		// creates a new PlaylistLinkEntry object and sets the values for
		// title and description using the method parameters
		PlaylistLinkEntry playListToAdd = new PlaylistLinkEntry();
		playListToAdd.setTitle(new PlainTextConstruct(title));
		playListToAdd.setSummary(new PlainTextConstruct(description));

		PlaylistLinkEntry createdPlayList = null;
		try {
			// adds the new playlist for the user
			createdPlayList = service.insert(new URL(playlistUrl), playListToAdd);

			logger.debug("playlist created: " + createdPlayList.getHtmlLink().getHref());
			createdPlayListId = createdPlayList.getPlaylistId();

		} catch (MalformedURLException e) {
			throw new YouTubeException("Could not create a new playlist: " + e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException("Could not create a new playlist: " + e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException("Could not create a new playlist: " + e.getMessage(), e.getCause());
		}

		return createdPlayListId;

	}

	@Override
	public void deletePlayList(YouTubeUser user, String playListID) throws YouTubeException, YouTubeAuthException {
		logger.debug("deletePlayList..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the playlist id has the correct pattern
		if (!playlistIdIsCorrect(playListID)) {
			throw new YouTubeException("playlist id is not correct");
		}

		// url for editing the playlists from the current user
		String playlistUrl = USER_FEED_PREFIX + "default/playlists/"
				+ playListID;

		PlaylistLinkEntry playlistToDelete;
		try {
			// gets the playlist to delete...
			playlistToDelete = service.getEntry(new URL(playlistUrl), PlaylistLinkEntry.class);

			logger.debug("deleting playlist '" + playListID + "'...");
			// deletes the playlist
			playlistToDelete.delete();
			logger.debug("playlist deleted.");

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public void addVideoPlayList(YouTubeUser user, String videoID, String playListID) throws YouTubeException, YouTubeAuthException {
		logger.debug("addVideoPlayList..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the video id has the correct pattern
		if (!videoIdIsCorrect(videoID, "video")) {
			throw new YouTubeException("video id is not correct");
		}

		// checks if the playlist id has the correct pattern
		if (!playlistIdIsCorrect(playListID)) {
			throw new YouTubeException("playlist id is not correct");
		}

		// url for getting the playlist
		String playlistUrl = PLAYLIST_FEED_PREFIX + playListID;

		// url for getting the video entry
		String videoEntryUrl = VIDEOS_FEED + videoID;

		VideoEntry videoToAddPlaylist;
		try {
			// gets the video entry to add to the playlist
			videoToAddPlaylist = getYouTubeVideoEntry(videoEntryUrl);

			// creates the playlist entry from the video entry
			PlaylistEntry playlistEntryToAdd = null;
			playlistEntryToAdd = new PlaylistEntry(videoToAddPlaylist);

			logger.debug("adding video '" + videoID + "' to a playlist '" + playListID + "' ...");
			// adds the video (playlist entry) to the playlist
			service.insert(new URL(playlistUrl), playlistEntryToAdd);
			logger.debug("video added to playlist");

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public void deleteVideoPlayList(YouTubeUser user, String playlistVideoID, String playListID) throws YouTubeException, YouTubeAuthException {
		logger.debug("deleteVideoPlayList..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// checks if the playlist id has the correct pattern
		if (!playlistIdIsCorrect(playListID)) {
			throw new YouTubeException("playlist id is not correct");
		}

		// url for getting the playlist
		String playlistUrl = PLAYLIST_FEED_PREFIX + playListID;

		// url for getting the video from the playlist
		String playlistEntryUrl = playlistUrl + "/" + playlistVideoID;

		// gets the playlist entry to delete
		PlaylistEntry playListEntryToDelete = null;
		playListEntryToDelete = getYouTubePlaylistEntry(playlistEntryUrl);

		// if the entry to delete exists..
		if (playListEntryToDelete != null) {

			try {
				// deletes the entry from the playlist
				logger.debug("deleting video '" + playlistVideoID + "' from the playlist '" + playListID + "' ...");
				playListEntryToDelete.delete();
				logger.debug("video deleted.");

			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

		} else {
			throw new YouTubeException("video to delete not found.");
		}

	}

	@Override
	public List<YouTubeContact> getContacts(YouTubeUser user) throws YouTubeException, YouTubeAuthException {
		logger.debug("getContacts..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubeContact> userContactsList = new ArrayList<YouTubeContact>();

		// gets the YouTube contact list of the user (FriendEntry object list)
		List<FriendEntry> friendEntryList = getYouTubeContacts();

		// if the user has contacts...
		if (!friendEntryList.isEmpty()) {
			// converts the FriendEntry object list into a model contact list
			userContactsList = convertFriendEntryListToModelContactList(friendEntryList);

		} else {
			logger.debug("this user has no contacts");
		}

		return userContactsList;
	}

	@Override
	public void addContact(YouTubeUser user, String username) throws YouTubeException, YouTubeAuthException {
		logger.debug("addContact..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// url for editing the contacts from the current user
		String contactFeedUrl = USER_FEED_PREFIX + "default/contacts";

		// url for getting the contact entry (FriendEntry) to add
		String userEntryUrl = USER_FEED_PREFIX + username;

		// gets the user entry to add as contact if exists
		FriendEntry userEntryToAdd = null;
		userEntryToAdd = getYouTubeFriendEntry(userEntryUrl);

		// if the user to add as contact exists...

		// searches if the user to add is already contact of the current
		// authenticated user
		FriendEntry friendEntryFound = null;
		friendEntryFound = searchUser(username);

		// if the contact not exist yet...
		if (friendEntryFound == null
				&& !userEntryToAdd.getUsername().equals(user.getUserName())) {

			// creates a FriendEntry and sets the contact category to
			// 'Friends'
			FriendEntry newFriend = new FriendEntry();
			com.google.gdata.data.Category contactCategory;
			contactCategory = new com.google.gdata.data.Category(YouTubeNamespace.CONTACT_LIST_SCHEME, "Friends");
			newFriend.getCategories().add(contactCategory);

			// sets the username and email if exists
			newFriend.setUsername(username);
			if (userEntryToAdd.getEmail() != null) {
				newFriend.setUsername(userEntryToAdd.getEmail().getAddress());
			}

			try {
				// adds the new contact created
				logger.debug("add contact " + username + " ...");
				service.insert(new URL(contactFeedUrl), newFriend);
				logger.debug("contact added");

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

		} else {
			if (userEntryToAdd.getUsername().equals(user.getUserName())) {
				throw new YouTubeException("can not add yourself as contact");
			} else {
				throw new YouTubeException("that user already exists as contact");
			}
		}

	}

	@Override
	public void deleteContact(YouTubeUser user, String userName) throws YouTubeException, YouTubeAuthException {
		logger.debug("deleteContact..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// searches if the user to delete is contact of the current
		// authenticated user and gets the entry
		FriendEntry friendEntryFound = new FriendEntry();
		friendEntryFound = searchUser(userName);

		// if the contact exists...
		if (friendEntryFound != null) {
			try {
				// deletes the contact from the user contact list
				logger.debug("deleting contact " + userName + "...");
				friendEntryFound.delete();
				logger.debug("contact deleted");

			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

		} else {

			throw new YouTubeException("user to delete not found");
		}

	}

	@Override
	public void acceptContact(YouTubeUser user, String userName, boolean accept) throws YouTubeAuthException, YouTubeException {
		logger.debug("acceptContact..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// searches the contact to accept his friend request
		FriendEntry friendEntryFound = new FriendEntry();
		friendEntryFound = searchUser(userName);

		// if the user exists...
		if (friendEntryFound != null) {
			// if the user did a friend request...
			if (friendEntryFound.getStatus() != null && friendEntryFound.getStatus().equals(YtStatus.Value.REQUESTED)) {

				// accepts the request if accept is true
				if (accept) {
					friendEntryFound.setStatus(YtStatus.Value.ACCEPTED);
					logger.debug("friend request from " + userName	+ " accepted");

					// rejects the request in other case
				} else {
					logger.debug("friend request from " + userName	+ " rejected");
					friendEntryFound.setStatus(YtStatus.Value.REJECTED);
					// the status will set to null although the user that sends
					// the friend request will see the status from the other
					// user as PENDING

				}
				try {
					// updates the contact with the status established
					friendEntryFound.update();

				} catch (IOException e) {
					throw new YouTubeException(e.getMessage(), e.getCause());
				} catch (ServiceException e) {
					throw new YouTubeException(e.getMessage(), e.getCause());
				}

			} else {
				throw new YouTubeException("this user did not make a friend request");
			}
		} else {
			throw new YouTubeException("this user is not your contact");
		}

	}

	@Override
	public YouTubeUser getUserProfile(Integer userId)throws YouTubeException, YouTubeAuthException {
		logger.debug("getUserProfile..");

		// checks if the user has YouTube account and was stored in the BBDD
		YoutubeUser youtubeUserStored = getYoutubeUser(userId);
		if (youtubeUserStored == null) {
			throw new YouTubeAuthException("invalid username, you need to register");
		}

		// checks if user exists and sets the credentials
		checkUserCredentials(youtubeUserStored.getUsername(), youtubeUserStored.getPass());
				
		// url for getting the user profile
		String profileUrl = USER_FEED_PREFIX + "default";

		UserProfileEntry profileEntry;
		try {
			// gets the YouTube user profile
			profileEntry = service.getEntry(new URL(profileUrl), UserProfileEntry.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// converts the YouTube user profile into the model user profile
		YouTubeUser modelProfile = convertYouTubeProfileToModelProfile(profileEntry);

		// adds the parameters that not appears in the YouTube profile
		modelProfile.setPassword(youtubeUserStored.getPass());
		modelProfile.setYouTubeId(youtubeUserStored.getYoutubeid());
		modelProfile.setUserId(youtubeUserStored.getUserid());

		return modelProfile;
	}

	@Override
	public StandarVideos getStandarVideos()	throws YouTubeException {
		logger.debug("getStandarVideos..");

		// sets the anonymous user
		resetYouTubeService();

		StandarVideos modelStandarVideos = new StandarVideos();

		List<YouTubeVideoMinimal> modelMostViewedVideoList = null;
		List<YouTubeVideoMinimal> modelRecentVideoList = null;

		// gets the most viewed video list with the first hundred elements and
		// adds them to the StandarVideos object
		modelMostViewedVideoList = getModelMostViewedVideos(100);
		modelStandarVideos.setMostViewed(modelMostViewedVideoList);

		// gets the most recent video list with the first hundred elements and
		// adds them to the StandarVideos object
		modelRecentVideoList = getModelRecentVideos(100);
		modelStandarVideos.setRecent(modelRecentVideoList);

		return modelStandarVideos;
	}

	@Override
	public void setPreferences(Integer userId, List<String> preferences) throws YouTubeException {
		logger.debug("setPreferences..");

		// preference module interfaz
		IYouTubeUserModel ytum = null;

		try {
			// gets an instance of the preference module
			ytum = new YoutubeUserModelImpl(youTubeDB, userId);

			// sets the preferences of the user
			ytum.setKeywordSelection(preferences);

			// updates the preferences on the BBDD
			ytum.saveUserModel();
			ytum = null;

		} catch (YouTubeException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public void setMarkPreferences(Integer userId, List<String> preferences, boolean value) throws YouTubeException {
		logger.debug("setMarkPreferences..");

		IYouTubeUserModel ytum = null;

		try {
			// gets an instance of the preference module
			ytum = new YoutubeUserModelImpl(youTubeDB, userId);

			// sets the preferences of the user
			ytum.setKeywordEvaluation(preferences, value);
			// updates the preferences on the BBDD
			ytum.saveUserModel();
			ytum = null;

		} catch (YouTubeException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

	}

	@Override
	public UserPreferences getPreferences(Integer userId) throws YouTubeException {
		logger.debug("getPreferences..");

		IYouTubeUserModel ytum = null;
		List<Preference> preferenceList = null;
		UserPreferences userPreferences = null;

		try {

			// gets an instance of the preference module
			ytum = new YoutubeUserModelImpl(youTubeDB, userId);

			// gets the preferences from the user
			preferenceList = ytum.getKeywordPreference();

			// converts the preference list in to a model preference list
			userPreferences = convertModulePreferencesToModelPreferences(preferenceList);

		} catch (YouTubeException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return userPreferences;
	}

	/**
	 * converts the module preference list into the object with the model
	 * preference list
	 * 
	 * @param preferenceList
	 *            module Preference object list
	 * @return an UserPreferences object with the preferences
	 */
	private UserPreferences convertModulePreferencesToModelPreferences(List<Preference> preferenceList) {		
		List<YouTubePreferences> tempPreferenceList = new ArrayList<YouTubePreferences>();

		if (!preferenceList.isEmpty()) {

			// gets the iterator from the module preference list
			Iterator<Preference> preferenceListIterator = preferenceList.iterator();
			YouTubePreferences currentYouTubePreference = null;
			while (preferenceListIterator.hasNext()) {
				Preference modulePreference = preferenceListIterator.next();
				
				currentYouTubePreference = new YouTubePreferences();
				currentYouTubePreference.setName(modulePreference.getName());
				currentYouTubePreference.setValue(modulePreference.getValue());
				
				tempPreferenceList.add(currentYouTubePreference);
			}
		}
		// adds the model preference list to the UserPreferences object
		UserPreferences userPreferences = new UserPreferences();
		userPreferences.setPreferences(tempPreferenceList);

		return userPreferences;
	}

	/**
	 * sets the YouTube account username and pass for operations that need
	 * authentication
	 * 
	 * @throws YouTubeAuthException
	 *             If the service is unable to authenticate the user
	 */
	private void checkUserCredentials(String username, String pass) throws YouTubeAuthException {

		if (username == null || pass == null) {
			throw new YouTubeAuthException("invalid username or password, you need to register");

		} else {
			try {
				service.setUserCredentials(username, pass);

			} catch (AuthenticationException e) {
				throw new YouTubeAuthException(e.getMessage(), e.getCause());
			}
		}
	}

	/**
	 * Reset YouTubeService
	 */
	private void resetYouTubeService() {
		service = null;
		service = new YouTubeService(SERVICENAME, DEVKEY);
	}


	/**
	 * 
	 * Gets a Youtube user
	 * 
	 * @param userId
	 *            id for user
	 * 
	 * @return a YoutubeUser object with the information about user or null if not exists
	 * @throws YouTubeException
	 *             If any error in the checking appears
	 * 
	 */
	private YoutubeUser getYoutubeUser(Integer userId) throws YouTubeException {
		// gets the EntityManager
		EntityManager em = youTubeDB.getEntityManager();

		// checks whether the user was already stored
		YoutubeUser youtubeuser = youTubeDB.selectYouTubeUserQuery(em, userId);

		// close the EntityManager
		youTubeDB.closeEntityManager(em);

		return youtubeuser;
	}


	/**
	 * @param youtubeVideoId
	 * @return
	 * @throws YouTubeException
	 */
	private VideoEntry getVideoEntryFromYoutubeVideoId(String youtubeVideoId) throws YouTubeException {
		VideoEntry videoEntry = null;

		// if video id is correct..
		if (youtubeVideoId != null && !youtubeVideoId.isEmpty()) {

			try {
				// creates the url for the video feed
				String entryUrl = VIDEOS_FEED + youtubeVideoId;

				// gets the video entry from the url
				videoEntry = getYouTubeVideoEntry(entryUrl);

				// converts the video entry into a model video
				// modelVideo = convertVideoEntryToModelVideo(videoEntry);

			} catch (IndexOutOfBoundsException e) {
				throw new YouTubeException("Could not get the video id from the video url: "
						+ e.getMessage(), e.getCause());
			}

		} else {
			throw new YouTubeException("video id is empty");
		}

		return videoEntry;

	}

	/**
	 * gets the video id depending on whether the video is a favorite, a
	 * playlist entry or a normal video
	 * 
	 * @param YouTubeId
	 *            the YouTube id with the video id to get
	 * @return the video id
	 */
	private String getVideoId(String YouTubeId) {

		// YouTube id examples depending on the kind of video:
		// tag:youtube.com,2008:video:NB0Ti6SwVTc
		// tag:youtube.com,2008:favorite:vjVQa1PpcFPjHbOE-_1WAetDNZ-zOGBNMu2672BsNFo
		// tag:youtube.com,2008:playlist:7F3C6FF6B9F0ED85:5B9E9A67F0A14C31

		// the video id is always located after the last ':'

		int pos = YouTubeId.lastIndexOf(":");

		String videoId = YouTubeId.substring(pos + 1);

		return videoId;
	}

	/**
	 * gets a YouTube video entry from an url
	 * 
	 * @param entryUrl
	 *            url for getting the video entry
	 * @return a YouTube VideoEntry object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private VideoEntry getYouTubeVideoEntry(String entryUrl) throws YouTubeException {

		VideoEntry videoEntry = null;

		try {
			// gets the video entry for the url
			videoEntry = service.getEntry(new URL(entryUrl), VideoEntry.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return videoEntry;
	}

	/**
	 * gets a YouTube playlist entry from an url
	 * 
	 * @param entryUrl
	 *            url for getting the playlist entry
	 * @return a YouTube PlaylistEntry object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private PlaylistEntry getYouTubePlaylistEntry(String entryUrl) throws YouTubeException {
		PlaylistEntry playListEntry = null;

		try {
			// gets the playlist entry for the url
			playListEntry = service.getEntry(new URL(entryUrl),	PlaylistEntry.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return playListEntry;
	}

	/**
	 * gets a YouTube playlist feed from an url
	 * 
	 * @param feedUrl
	 *            url for getting the playlist feed
	 * @return a YouTube PlaylistFeed object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private PlaylistFeed getYouTubePlaylistFeed(String feedUrl)	throws YouTubeException {

		PlaylistFeed playlistFeed = null;

		try {
			playlistFeed = service
					.getFeed(new URL(feedUrl), PlaylistFeed.class);
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return playlistFeed;
	}

	/**
	 * gets the feed with the playlists from an url
	 * 
	 * @param feedUrl
	 *            url for getting the playlist feed
	 * @return a YouTube PlaylistLinkFeed object with the playlists
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private PlaylistLinkFeed getYouTubePlaylistLinkFeed(String feedUrl)	throws YouTubeException {

		PlaylistLinkFeed playlistLinkFeed = null;

		try {
			// gets the playlist feed
			playlistLinkFeed = service.getFeed(new URL(feedUrl), PlaylistLinkFeed.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return playlistLinkFeed;
	}

	/**
	 * gets a YouTube video feed from an url
	 * 
	 * @param feedUrl
	 *            url for getting the video feed
	 * @return a YouTube VideoFeed object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private VideoFeed getYouTubeVideoFeed(String feedUrl)throws YouTubeException {

		VideoFeed videoFeed = null;

		try {
			// gets the video feed
			videoFeed = service.getFeed(new URL(feedUrl), VideoFeed.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return videoFeed;
	}

	/**
	 * gets all the video entries from a video feed
	 * 
	 * @param videoFeed
	 *            a YouTube VideoFeed object
	 * @return a YouTube VideoEntry object list with all the videos of the feed
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<VideoEntry> getTotalVideoEntries(VideoFeed videoFeed)throws YouTubeException {

		List<VideoEntry> totalVideoEntryList = new ArrayList<VideoEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalVideoEntryList.addAll(videoFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = videoFeed.getNextLink();

		while (nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				videoFeed = service.getFeed(new URL(nextFeedUrl), VideoFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalVideoEntryList.addAll(videoFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = videoFeed.getNextLink();

			/*
			 * // to delete... if (nextLinkFeed != null) {
			 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
			 */
		}

		return totalVideoEntryList;

	}

	/**
	 * gets a number of entries of the video feed if exists
	 * 
	 * @param videoFeed
	 *            a YouTube VideoFeed object that contains the videos
	 * @param numEntries
	 *            number of entries to get if exists. Must be multiple of 25
	 * @return a YouTube VideoEntry object list with the videos
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */

	private List<VideoEntry> getVideoEntries(VideoFeed videoFeed, Integer numEntries) throws YouTubeException {
		List<VideoEntry> totalVideoEntryList = new ArrayList<VideoEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalVideoEntryList.addAll(videoFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = videoFeed.getNextLink();

		/*
		 * // to delete.. if (nextLinkFeed != null) {
		 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
		 */

		// for getting the rest of entries
		// the number must be multiple of 25
		int totalPages = (numEntries / 25) - 1;

		while (totalPages > 0 && nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				videoFeed = service.getFeed(new URL(nextFeedUrl), VideoFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalVideoEntryList.addAll(videoFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = videoFeed.getNextLink();

			totalPages--;

			/*
			 * // to delete... if (nextLinkFeed != null) {
			 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
			 */
		}

		return totalVideoEntryList;
	}

	/**
	 * gets a YouTube friend entry from an url
	 * 
	 * @param entryUrl
	 *            url for getting the friend entry
	 * @return a YouTube FriendEntry object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private FriendEntry getYouTubeFriendEntry(String entryUrl)	throws YouTubeException {

		FriendEntry friendEntry = null;

		try {
			// gets the friend entry for the url
			friendEntry = service.getEntry(new URL(entryUrl), FriendEntry.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return friendEntry;

	}

	/**
	 * gets all the entries from a YouTube playlist
	 * 
	 * @param playListFeed
	 *            a YouTube PlaylistFeed object with entries (videos) of a
	 *            playlist
	 * @param numEntries
	 *            number of entries to get if exists. Must be multiple of 25
	 * @return a YouTube PlaylistEntry object list with the videos
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<PlaylistEntry> getTotalPlayListEntries(PlaylistFeed playListFeed, Integer numEntries)	throws YouTubeException {
		List<PlaylistEntry> totalPlayListEntryList = new ArrayList<PlaylistEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalPlayListEntryList.addAll(playListFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = playListFeed.getNextLink();

		// for getting the rest of entries
		// the number must be multiple of 25
		int totalPages = (numEntries / 25) - 1;

		while (totalPages > 0 && nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				playListFeed = service.getFeed(new URL(nextFeedUrl), PlaylistFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalPlayListEntryList.addAll(playListFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = playListFeed.getNextLink();

			/*
			 * // to delete... if (nextLinkFeed != null) {
			 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
			 */

			totalPages--;

		}

		return totalPlayListEntryList;

	}

	/**
	 * gets a number of playlists from a feed
	 * 
	 * @param playListLinkFeed
	 *            a YouTube PlaylistLinkFeed object that contains the playlists
	 * @param numEntries
	 *            number of entries to get if exists. Must be multiple of 25
	 * @return a list of YouTube PlaylistLinkEntry objects that represents each
	 *         playlist
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<PlaylistLinkEntry> getPlayListLinkEntries(PlaylistLinkFeed playListLinkFeed, Integer numEntries)throws YouTubeException {
		List<PlaylistLinkEntry> totalPlayListLinkEntryList = new ArrayList<PlaylistLinkEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalPlayListLinkEntryList.addAll(playListLinkFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = playListLinkFeed.getNextLink();

		// for getting the rest of entries
		// the number must be multiple of 25
		int totalPages = (numEntries / 25) - 1;

		while (totalPages > 0 && nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				playListLinkFeed = service.getFeed(new URL(nextFeedUrl), PlaylistLinkFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalPlayListLinkEntryList.addAll(playListLinkFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = playListLinkFeed.getNextLink();

			totalPages--;

			/*
			 * // to delete... if (nextLinkFeed != null) {
			 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
			 */
		}

		return totalPlayListLinkEntryList;

	}

	/**
	 * gets all the playlists from a feed
	 * 
	 * @param playListLinkFeed
	 *            a YouTube PlaylistLinkFeed object that contains the playlists
	 * @return a list of YouTube PlaylistLinkEntry objects that represents each
	 *         playlist
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<PlaylistLinkEntry> getTotalPlayListLinkEntries(PlaylistLinkFeed playListLinkFeed) throws YouTubeException {

		List<PlaylistLinkEntry> totalPlayListLinkEntryList = new ArrayList<PlaylistLinkEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalPlayListLinkEntryList.addAll(playListLinkFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = playListLinkFeed.getNextLink();

		while (nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				playListLinkFeed = service.getFeed(new URL(nextFeedUrl), PlaylistLinkFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalPlayListLinkEntryList.addAll(playListLinkFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = playListLinkFeed.getNextLink();

			/*
			 * // to delete... if (nextLinkFeed != null) {
			 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
			 */
		}

		return totalPlayListLinkEntryList;

	}

	/**
	 * gets a number of comments from a feed
	 * 
	 * @param commentFeed
	 *            a YouTube CommentFeed object that contains the comments
	 * @param numEntries
	 *            number of entries to get if exists. Must be multiple of 25
	 * @return a list of YouTube CommentEntry objects that represents each
	 *         comment
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<CommentEntry> getCommentEntries(CommentFeed commentFeed, Integer numEntries) throws YouTubeException {

		List<CommentEntry> totalCommentEntryList = new ArrayList<CommentEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalCommentEntryList.addAll(commentFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = commentFeed.getNextLink();

		/*
		 * // to delete.. if (nextLinkFeed != null) {
		 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
		 */

		// for getting the rest of entries
		// the number must be multiple of 25
		int totalPages = (numEntries / 25) - 1;

		while (totalPages > 0 && nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				commentFeed = service.getFeed(new URL(nextFeedUrl), CommentFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalCommentEntryList.addAll(commentFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = commentFeed.getNextLink();

			totalPages--;

			/*
			 * // to delete... if (nextLinkFeed != null) {
			 * logger.info("nextLinkFeed " + nextLinkFeed.getHref()); }
			 */
		}

		return totalCommentEntryList;
	}

	/**
	 * gets a YouTube comment feed from an url
	 * 
	 * @param feedUrl
	 *            url for getting the comment feed
	 * @return a YouTube CommentFeed object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private CommentFeed getYouTubeCommentFeed(String feedUrl) throws YouTubeException {
		CommentFeed commentFeed;

		try {
			// gets the comment feed
			commentFeed = service.getFeed(new URL(feedUrl), CommentFeed.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return commentFeed;
	}

	/**
	 * gets a model most viewed video list
	 * 
	 * @return a model YouTubeVideoMinimal object list that contains the videos
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<YouTubeVideoMinimal> getModelMostViewedVideos(Integer numElements) throws YouTubeException {
		List<YouTubeVideoMinimal> modelMostViewedVideoList = new ArrayList<YouTubeVideoMinimal>();

		// url for getting most viewed video feed
		String mostViewedUrl = STANDARD_FEED + "most_viewed"; // ES/most_viewed?time=all_time

		// gets the most viewed video feed
		VideoFeed mostViewedFeed = null;
		mostViewedFeed = getYouTubeVideoFeed(mostViewedUrl);

		// gets the list of the first hundred videos from the feed
		List<VideoEntry> mostViewedVideoList = null;
		mostViewedVideoList = getVideoEntries(mostViewedFeed, numElements);

		// converts the YouTube video list into a model minimal video list
		modelMostViewedVideoList = convertVideoEntryListToModelVideoMinimalList(mostViewedVideoList);

		return modelMostViewedVideoList;
	}

	/**
	 * gets a model most recent video list
	 * 
	 * @return a model YouTubeVideoMinimal object list that contains the videos
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<YouTubeVideoMinimal> getModelRecentVideos(Integer numElements)	throws YouTubeException {

		List<YouTubeVideoMinimal> modelRecentVideoList = new ArrayList<YouTubeVideoMinimal>();

		// url for getting most recent video feed
		String mostRecentUrl = STANDARD_FEED + "most_recent";

		// gets the most recent video feed
		VideoFeed mostRecentFeed = null;
		mostRecentFeed = getYouTubeVideoFeed(mostRecentUrl);

		// gets the list of the first hundred videos from the feed
		List<VideoEntry> mostRecentVideoList = null;
		mostRecentVideoList = getVideoEntries(mostRecentFeed, numElements);

		// converts the YouTube video list into a model minimal video list
		modelRecentVideoList = convertVideoEntryListToModelVideoMinimalList(mostRecentVideoList);

		return modelRecentVideoList;
	}

	/**
	 * converts a YouTube user Status into a model user Status
	 * 
	 * @param relationship
	 *            a YouTube YtRelationship.Status object
	 * @return a model YouTubeUser.Status object
	 */
	private YouTubeUser.Status convertYouTubeUserStatusToModelUserStatus(YtRelationship.Status relationship) {

		YouTubeUser.Status modelUserStatus = null;

		// gets the readable name from the YouTube user Status object
		String stringContactStatus = relationship.toString();

		// sets the correct model user Status
		if (stringContactStatus.equals("OPEN")) {
			modelUserStatus = YouTubeUser.Status.OPEN;
		} else if (stringContactStatus.equals("SINGLE")) {
			modelUserStatus = YouTubeUser.Status.SINGLE;
		} else if (stringContactStatus.equals("TAKEN")) {
			modelUserStatus = YouTubeUser.Status.TAKEN;
		}

		return modelUserStatus;

	}

	/**
	 * converts a YouTube user Gender into a model user Gender
	 * 
	 * @param youTubeGender
	 *            a YouTube YtGender.Value object
	 * @return a model YouTubeUser.Gender object
	 */
	private YouTubeUser.Gender convertYouTubeGenderToModelGender(YtGender.Value youTubeGender) {

		YouTubeUser.Gender modelUserGender = null;

		// gets the readable name from the YouTube user Gender object
		String stringUserGender = youTubeGender.toString();

		// sets the correct model user Gender
		if (stringUserGender.equals("FEMALE")) {
			modelUserGender = YouTubeUser.Gender.FEMALE;
		} else if (stringUserGender.equals("MALE")) {
			modelUserGender = YouTubeUser.Gender.MALE;
		}

		return modelUserGender;

	}

	/**
	 * converts the YouTube user profile into a model user profile
	 * 
	 * @param profileEntry
	 *            a YouTube UserProfileEntry object
	 * @return a model YouTubeUser object
	 */
	private YouTubeUser convertYouTubeProfileToModelProfile(UserProfileEntry profileEntry) {

		YouTubeUser modelProfile = new YouTubeUser();

		// sets the user status
		YtRelationship.Status youTubeUserStatus = profileEntry
				.getRelationship();
		YouTubeUser.Status userStatus = null;
		if (youTubeUserStatus != null) {
			userStatus = convertYouTubeUserStatusToModelUserStatus(youTubeUserStatus);
		}
		modelProfile.setStatus(userStatus);

		// sets the user gender
		YouTubeUser.Gender modelGender = null;
		YtGender.Value youTubeGender = profileEntry.getGender();
		if (youTubeGender != null) {
			modelGender = convertYouTubeGenderToModelGender(youTubeGender);
		}
		modelProfile.setGender(modelGender);

		// sets the rest of user attributes
		modelProfile.setDescription(profileEntry.getAboutMe());
		modelProfile.setAge(profileEntry.getAge());
		modelProfile.setName(profileEntry.getFirstName());
		modelProfile.setLastName(profileEntry.getLastName());
		modelProfile.setUserName(profileEntry.getUsername());
		modelProfile.setCompany(profileEntry.getCompany());
		modelProfile.setFotoLink(profileEntry.getThumbnail().getUrl());
		modelProfile.setMusic(profileEntry.getMusic());
		modelProfile.setMovies(profileEntry.getMovies());
		modelProfile.setOccupation(profileEntry.getOccupation());
		modelProfile.setHobbies(profileEntry.getHobbies());
		modelProfile.setHome(profileEntry.getHometown());

		return modelProfile;
	}

	/**
	 * searches a YouTube contact from the contact list of the current user
	 * 
	 * @param userName
	 *            username from the user to search
	 * @return a FriendEntry object if the user exists or null in other case
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private FriendEntry searchUser(String userName) throws YouTubeException {
		FriendEntry friendEntryFound = new FriendEntry();

		// url for getting the contact from the user contact list if exists
		String contactExistsUrl = USER_FEED_PREFIX + "default/contacts/"+ userName;

		try {
			// gets the contact entry
			friendEntryFound = getYouTubeFriendEntry(contactExistsUrl);

		} catch (YouTubeException e) {

			// if the exception is 'Contact not found', the contact not exist
			// for this user
			if (e.getMessage().equals("Contact not found")) {
				friendEntryFound = null;
				logger.debug("contact not found");

			} else {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}
		}

		return friendEntryFound;
	}

	/**
	 * gets all the contact entries from a contact feed
	 * 
	 * @param friendFeed
	 *            a YouTube FriendFeed object
	 * @return a YouTube FriendEntry object list with all the contacts of the
	 *         feed
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<FriendEntry> getTotalContactEntries(FriendFeed friendFeed)	throws YouTubeException {

		List<FriendEntry> totalFriendEntryList = new ArrayList<FriendEntry>();
		String nextFeedUrl = null;

		// adds the first 25 entries of the feed to the list
		totalFriendEntryList.addAll(friendFeed.getEntries());

		// gets the link to the next 25 entries
		Link nextLinkFeed = friendFeed.getNextLink();

		// to delete..
		/*
		 * if (nextLinkFeed != null) { logger.info("nextLinkFeed " +
		 * nextLinkFeed.getHref()); }
		 */

		while (nextLinkFeed != null) {

			// as the link exists, gets the url for the following 25 entries
			// from the feed
			nextFeedUrl = nextLinkFeed.getHref();

			try {
				// gets the feed with the following 25 entries
				friendFeed = service.getFeed(new URL(nextFeedUrl), FriendFeed.class);

			} catch (MalformedURLException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			} catch (ServiceException e) {
				throw new YouTubeException(e.getMessage(), e.getCause());
			}

			// adds the following 25 entries from the feed
			totalFriendEntryList.addAll(friendFeed.getEntries());

			// gets the link for the following 25 entries from the feed
			nextLinkFeed = friendFeed.getNextLink();

			// to delete...
			/*
			 * if (nextLinkFeed != null) { logger.info("nextLinkFeed " +
			 * nextLinkFeed.getHref()); }
			 */

		}

		return totalFriendEntryList;
	}

	/**
	 * gets the YouTube contact list from the current authenticated user
	 * 
	 * @return a FriendEntry object list
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<FriendEntry> getYouTubeContacts() throws YouTubeException {

		List<FriendEntry> friendEntryList = new ArrayList<FriendEntry>();

		// url for getting the contact list of the current user
		String contactFeedUrl = USER_FEED_PREFIX + "default/contacts";

		FriendFeed friendFeed = null;
		try {

			// gets the feed with the contacts (FriendEntry objects)
			friendFeed = service.getFeed(new URL(contactFeedUrl), FriendFeed.class);

		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// gets the contact list from the feed
		// friendEntryList = friendFeed.getEntries();
		friendEntryList = getTotalContactEntries(friendFeed);

		return friendEntryList;
	}

	/**
	 * converts a FriendEntry object list into a model YouTubeContact object
	 * list
	 * 
	 * @param friendEntryList
	 *            a FriendEntry object list
	 * @return a model YouTubeContact object list
	 */
	private List<YouTubeContact> convertFriendEntryListToModelContactList(List<FriendEntry> friendEntryList) {

		List<YouTubeContact> modelContactList = new ArrayList<YouTubeContact>();

		// gets the iterator from the FriendEntry list
		Iterator<FriendEntry> friendEntryListIterator = friendEntryList.iterator();

		YouTubeContact contactToAdd = null;

		// loops through the FriendEntry list,converts each item into a model
		// YouTubeContact object and adds them into a model YouTubeContact list
		while (friendEntryListIterator.hasNext()) {

			FriendEntry currentFriendEntry = friendEntryListIterator.next();

			contactToAdd = convertFriendEntryToModelContact(currentFriendEntry);
			modelContactList.add(contactToAdd);
		}

		return modelContactList;
	}

	/**
	 * converts a FriendEntry object into a model YouTubeContact object
	 * 
	 * @param friendEntry
	 *            a FriendEntry object
	 * @return a model YouTubeContact object
	 */
	private YouTubeContact convertFriendEntryToModelContact(FriendEntry friendEntry) {

		YouTubeContact modelContact = new YouTubeContact();

		// sets the email
		if (friendEntry.getEmail() != null) {
			modelContact.setEmail(friendEntry.getEmail().getAddress());
		}
		// sets the username
		modelContact.setUserName(friendEntry.getUsername());

		// sets the status
		Status modelContactStatus = null;
		if (friendEntry.getStatus() != null) {

			modelContactStatus = convertYouTubeContactStatusToModelContactStatus(friendEntry.getStatus());
		}

		modelContact.setStatus(modelContactStatus);

		return modelContact;
	}

	/**
	 * converts the YouTube contact status into a model contact status
	 * 
	 * @param status
	 *            a YouTube YouTubeContact.Status object
	 * @return a model YouTubeContact.Status object
	 */
	private YouTubeContact.Status convertYouTubeContactStatusToModelContactStatus(YtStatus.Value status) {

		Status modelContactStatus = null;

		// gets the readable name from the YouTube contact Status object
		String stringContactStatus = status.name();

		// sets the correct model contact Status
		if (stringContactStatus.equals("ACCEPTED")) {
			modelContactStatus = YouTubeContact.Status.ACCEPTED;
		} else if (stringContactStatus.equals("PENDING")) {
			modelContactStatus = YouTubeContact.Status.PENDING;
		} else if (stringContactStatus.equals("REJECTED")) {
			modelContactStatus = YouTubeContact.Status.REJECTED;
		} else if (stringContactStatus.equals("REQUESTED")) {
			modelContactStatus = YouTubeContact.Status.REQUESTED;

		}

		return modelContactStatus;
	}

	/**
	 * checks if a video id is correct
	 * 
	 * @param videoID
	 *            video id
	 * @param type
	 *            type of video id to check
	 * @return true if the video id is correct, false in other case
	 */
	private boolean videoIdIsCorrect(String videoID, String type) {
		boolean isCorrect = false;

		// if video id is null, the pattern will be not correct
		if (videoID == null) {
			isCorrect = false;
		}

		// if the first character is blank, the pattern will be not correct
		else if (videoID.charAt(0) == ' ') {
			isCorrect = false;
		}

		// the length of a video id is always 11 characters
		else if (type.compareTo("video") == 0 && videoID.length() == 11) {
			isCorrect = true;

		}
		
		// 43 characters for a favorite video id
		else if (type.compareTo("favorite") == 0 && videoID.length() == 43) {
			isCorrect = true;

		}

		return isCorrect;
	}

	/**
	 * checks if a playList id is correct
	 * 
	 * @param playListID
	 *            playList id
	 * @return true if the playList id is correct, false in other case
	 */

	private boolean playlistIdIsCorrect(String playListID) {
		boolean isCorrect = true;

		// the length of a playList id is always 16 characters
		if (playListID == null || playListID.charAt(0) == ' ' || playListID.length() != 16) {
			isCorrect = false;
		}
		return isCorrect;

	}

	/**
	 * gets the video type for uploading a video
	 * 
	 * @param videoPath
	 *            the complete video path
	 * 
	 * @return the video type for YouTube
	 */
	private String getVideoType(String videoPath) {

		// gets the file extension
		String extension = videoPath.substring(videoPath.length() - 4,	videoPath.length());

		String videoType = null;

		// if extension has 3 characters
		if (extension.charAt(0) == '.') {
			extension = extension.substring(1);

			if (extension.equals("mov")) {
				videoType = "video/quicktime";
			} else if (extension.equals("avi")) {
				// video/msvideo
				// video/x-msvideo
				videoType = "video/avi";
			} else if (extension.equals("mpg")) {
				videoType = "video/mpg";
			} else if (extension.equals("mpeg")) {
				videoType = "video/mpeg";
			} else if (extension.equals("mp4")) {
				videoType = "video/mp4";
			} else if (extension.equals("flv")) {
				videoType = "video/x-flv";
			} else if (extension.equals("wmv")) {
				videoType = "video/x-ms-wmv";
			} else if (extension.equals("3gp")) {
				videoType = "video/3gpp";
			} else if (extension.equals("3g2")) {
				videoType = "video/3gpp2";
			}

		} else if (extension.equals("mpeg")) {
			videoType = "video/mpeg";
		}

		return videoType;
	}

	/**
	 * converts YouTube Person objects into model String authors
	 * 
	 * @param listPersons
	 *            an author list of YouTube Person objects
	 * @return a list of model String authors
	 */

	private List<String> convertPersonsToStringAuthors(List<Person> personList) {

		List<String> stringAuthorList = new ArrayList<String>();

		// gets the iterator from the Person object list
		Iterator<Person> personListIterator = personList.iterator();

		String stringAuthorToAdd = null;

		// loops through the Person list, converts each item into a
		// String author and adds them to a list of String author objects
		while (personListIterator.hasNext()) {

			stringAuthorToAdd = personListIterator.next().getName();
			stringAuthorList.add(stringAuthorToAdd);
		}

		return stringAuthorList;
	}

	/**
	 * converts a model Category object into a YouTube Category object
	 * 
	 * @param modelCategory
	 *            a model Category object
	 * @return a YouTube Category object
	 */
	private com.google.gdata.data.Category convertModelCategoryToYouTubeCategory(Category modelCategory) {

		com.google.gdata.data.Category youTubeCategory = new com.google.gdata.data.Category(YouTubeNamespace.CATEGORY_SCHEME, "Film");

		// gets the readable name from the Enum model Category object
		String stringModelCategory = modelCategory.getReadableName();

		// if the category selected is deprecated, the attribute
		// isCategoryDeprecated is set to true
		if (stringModelCategory.equals("Cine y animación")) {
			youTubeCategory.setTerm("Film");
		} else if (stringModelCategory.equals("Automoción")) {
			youTubeCategory.setTerm("Autos");
		} else if (stringModelCategory.equals("Música")) {
			youTubeCategory.setTerm("Music");
		} else if (stringModelCategory.equals("Mascotas y animales")) {
			youTubeCategory.setTerm("Animals");
		} else if (stringModelCategory.equals("Deportes")) {
			youTubeCategory.setTerm("Sports");
		} else if (stringModelCategory.equals("Viajes y eventos")) {
			youTubeCategory.setTerm("Travel");
		} else if (stringModelCategory.equals("Juegos")) {
			youTubeCategory.setTerm("Games");
		} else if (stringModelCategory.equals("Comedia")) {
			youTubeCategory.setTerm("Comedy");
		} else if (stringModelCategory.equals("Gente y Blogs")) {
			youTubeCategory.setTerm("People");
		} else if (stringModelCategory.equals("Noticias y Política")) {
			youTubeCategory.setTerm("News");
		} else if (stringModelCategory.equals("Ocio")) {
			youTubeCategory.setTerm("Entertainment");
		} else if (stringModelCategory.equals("Educación")) {
			youTubeCategory.setTerm("Education");
		} else if (stringModelCategory.equals("Ciencia y Tecnología")) {
			youTubeCategory.setTerm("Tech");
		} else if (stringModelCategory.equals("Instrucc. varias y estilos")) {
			youTubeCategory.setTerm("Howto");
		} else if (stringModelCategory.equals("ONGs y activismo")) {
			youTubeCategory.setTerm("Nonprofit");
		} else if (stringModelCategory.equals("Blogs de Vídeos")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Videoblog");
		} else if (stringModelCategory.equals("Cortometrajes")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Shortmov");
		} else if (stringModelCategory.equals("Películas")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies");
		} else if (stringModelCategory.equals("Películas animación")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Anime_animation");
		} else if (stringModelCategory.equals("Películas comedia")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Comedy");
		} else if (stringModelCategory.equals("Películas documental")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Documentary");
		} else if (stringModelCategory.equals("Películas acción/aventura")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Action_adventure");
		} else if (stringModelCategory.equals("Películas clásicos")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Classics");
		} else if (stringModelCategory.equals("Cine extranjero")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Foreign");
		} else if (stringModelCategory.equals("Películas terror")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Horror");
		} else if (stringModelCategory.equals("Películas drama")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Drama");
		} else if (stringModelCategory.equals("Películas familia")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Family");
		} else if (stringModelCategory.equals("Cortos")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Shorts");
		} else if (stringModelCategory.equals("Programas")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Shows");
		} else if (stringModelCategory.equals("Películas ciencia ficción")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Sci_fi_fantasy");
		} else if (stringModelCategory.equals("Películas suspense")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Movies_Thriller");
		} else if (stringModelCategory.equals("traíler")) {
			// setCategoryDeprecated(true);
			youTubeCategory.setTerm("Trailers");
		}

		return youTubeCategory;
	}

	/**
	 * converts a YouTube Category object into a model Category object. Filter
	 * the MediaCategories to get only the query categories
	 * 
	 * 
	 * @param youTubeCategory
	 *            a YouTube Category object
	 * @param isDeprecated
	 *            return true if the category is deprecated, false in other case
	 * @return a model Category object
	 */

	private Category convertYouTubeCategoryToModelCategory(com.google.gdata.data.Category youTubeCategory) {

		Category modelCategory = null;

		// gets the readable name from the YouTube Category object
		String stringCategory = youTubeCategory.getTerm();

		// if the category selected is deprecated, the attribute
		// isCategoryDeprecated is set to true
		if (stringCategory.equals("Film")) {
			modelCategory = Category.FILM;
		} else if (stringCategory.equals("Autos")) {
			modelCategory = Category.AUTOS;
		} else if (stringCategory.equals("Music")) {
			modelCategory = Category.MUSIC;
		} else if (stringCategory.equals("Animals")) {
			modelCategory = Category.ANIMALS;
		} else if (stringCategory.equals("Sports")) {
			modelCategory = Category.SPORT;
		} else if (stringCategory.equals("Travel")) {
			modelCategory = Category.TRAVEL;
		} else if (stringCategory.equals("Games")) {
			modelCategory = Category.GAMES;
		} else if (stringCategory.equals("Comedy")) {
			modelCategory = Category.COMEDY;
		} else if (stringCategory.equals("People")) {
			modelCategory = Category.PEOPLE;
		} else if (stringCategory.equals("News")) {
			modelCategory = Category.NEWS;
		} else if (stringCategory.equals("Entertainment")) {
			modelCategory = Category.ENTERTAINMENT;
		} else if (stringCategory.equals("Education")) {
			modelCategory = Category.EDUCATION;
		} else if (stringCategory.equals("Tech")) {
			modelCategory = Category.TECH;
		} else if (stringCategory.equals("Howto")) {
			modelCategory = Category.HOWTO;
		} else if (stringCategory.equals("Nonprofit")) {
			modelCategory = Category.NONPROFIT;
		} else if (stringCategory.equals("Movies")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES;
		} else if (stringCategory.equals("Shortmov")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.SHORTMOV;
		} else if (stringCategory.equals("Videoblog")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.VIDEOBLOG;
		} else if (stringCategory.equals("Movies_Anime_animation")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_ANIMATION;
		} else if (stringCategory.equals("Movies_Comedy")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_COMEDY;
		} else if (stringCategory.equals("Movies_Documentary")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_DOCUMENTARY;
		} else if (stringCategory.equals("Movies_Action_adventure")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_ACTION_ADVENTURE;
		} else if (stringCategory.equals("Movies_Classics")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_CLASSIC;
		} else if (stringCategory.equals("Movies_Foreign")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_FOREIGN;
		} else if (stringCategory.equals("Movies_Horror")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_HORROR;
		} else if (stringCategory.equals("Movies_Drama")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_DRAMA;
		} else if (stringCategory.equals("Movies_Family")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_FAMILY;
		} else if (stringCategory.equals("Movies_Shorts")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_SHORTS;
		} else if (stringCategory.equals("Shows")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.SHOWS;
		} else if (stringCategory.equals("Movies_Sci_fi_fantasy")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_SCI_FI_FANTASY;
		} else if (stringCategory.equals("Movies_Thriller")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.MOVIES_THRILLER;
		} else if (stringCategory.equals("Trailers")) {
			// setCategoryDeprecated(true);
			modelCategory = Category.TRAILER;
		}

		return modelCategory;
	}

	/**
	 * converts a YouTube Category list into a model Category list
	 * 
	 * @param youTubeCategoryList
	 *            a YouTube Category list
	 * @return a model Category list
	 */
	private List<Category> convertYouTubeCategoryListToModelCategoryList(List<com.google.gdata.data.Category> youTubeCategoryList) {

		List<Category> modelCategoryList = new ArrayList<Category>();

		// gets the iterator from the YouTube Category list
		Iterator<com.google.gdata.data.Category> youTubeCategoryListIterator = null;
		youTubeCategoryListIterator = youTubeCategoryList.iterator();

		com.google.gdata.data.Category currentYouTubeCategory = null;
		Category modelCategoryToAdd = null;

		// loops through the YouTube Category list, converts each item into a
		// model Category object if exists and adds them to a list of model
		// Category objects
		while (youTubeCategoryListIterator.hasNext()) {

			currentYouTubeCategory = youTubeCategoryListIterator.next();
			modelCategoryToAdd = convertYouTubeCategoryToModelCategory(currentYouTubeCategory);

			// because YouTube adds the video keywords to the category list
			if (modelCategoryToAdd != null) {
				modelCategoryList.add(modelCategoryToAdd);
			}
		}
		return modelCategoryList;
	}

	/**
	 * converts a model Category list into a YouTube Category list
	 * 
	 * @param modelCategoryList
	 *            a model Category list
	 * @return a YouTube Category list
	 */
	private List<com.google.gdata.data.Category> convertModelCategoryListToYouTubeCategoryList(List<Category> modelCategoryList) {

		List<com.google.gdata.data.Category> youTubeCategoryList = new ArrayList<com.google.gdata.data.Category>();

		// gets the iterator from the model Category list
		Iterator<Category> modelCategoryListIterator = modelCategoryList
				.iterator();

		Category currentModelCategory = null;
		com.google.gdata.data.Category youTubeCategoryToAdd = null;

		// loops through the model Category list, converts each item into a
		// YouTube Category object and adds them to a list of YouTube Category
		// objects
		while (modelCategoryListIterator.hasNext()) {

			currentModelCategory = modelCategoryListIterator.next();
			youTubeCategoryToAdd = convertModelCategoryToYouTubeCategory(currentModelCategory);

			if (youTubeCategoryToAdd != null) {
				youTubeCategoryList.add(youTubeCategoryToAdd);
			}
		}

		return youTubeCategoryList;

	}

	/**
	 * converts a model YouTubeVideo object into a VideoEntry object for
	 * uploading a video
	 * 
	 * @param modelVideo
	 *            model YouTubeVideo object
	 * @return a YouTube VideoEntry object
	 */
	private VideoEntry convertModelVideoToVideoEntry(YouTubeVideo modelVideo) {
		VideoEntry videoEntry = new VideoEntry();
		YouTubeMediaGroup mg = videoEntry.getOrCreateMediaGroup();

		// set the title
		mg.setTitle(new MediaTitle());
		mg.getTitle().setPlainTextContent(modelVideo.getTitle());
		// videoEntry.setTitle(TextConstruct.plainText(modelVideo.getTitle()));

		// sets the keywords
		mg.setKeywords(new MediaKeywords());
		mg.getKeywords().addKeywords(modelVideo.getKeyWords());

		// sets description
		mg.setDescription(new MediaDescription());
		mg.getDescription().setPlainTextContent(modelVideo.getDescription());

		// sets state
		boolean isPrivate = (modelVideo.getPublicationState() == State.PRIVATE);
		mg.setPrivate(isPrivate);

		// sets the categories
		List<com.google.gdata.data.Category> categories = convertModelCategoryListToYouTubeCategoryList(modelVideo.getCategories());
		mg.addCategory(new MediaCategory(YouTubeNamespace.CATEGORY_SCHEME, categories.get(0).getTerm()));

		return videoEntry;
	}

	/**
	 * converts a YouTube VideoEntry object into a model YouTubeVideo object
	 * 
	 * @return a model YouTubeVideo object
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private YouTubeVideo convertVideoEntryToModelVideo(VideoEntry videoEntry) throws YouTubeException {
		YouTubeVideo modelVideo = new YouTubeVideo();

		// sets the categories
		// logger.debug(videoEntry.getCategories());

		List<com.google.gdata.data.Category> youTubeCateroryList = new ArrayList<com.google.gdata.data.Category>(videoEntry.getCategories());
		modelVideo.setCategories(convertYouTubeCategoryListToModelCategoryList(youTubeCateroryList));

		// sets the first hundred comments
		List<CommentEntry> commentsList = getYouTubeComments(videoEntry, 100);
		modelVideo.setComments(convertCommentEntryListToModelCommentList(commentsList));

		// sets the rest of the attributes
		modelVideo.setTitle(videoEntry.getTitle().getPlainText());

		String youTubeVideoId = getVideoId(videoEntry.getId());
		modelVideo.setVideoId(youTubeVideoId);

		if (videoEntry.getStatistics() != null) {
			modelVideo.setViewCount(videoEntry.getStatistics().getViewCount());
		}

		modelVideo.setAuthors(convertPersonsToStringAuthors(videoEntry.getAuthors()));

		if (!videoEntry.getMediaGroup().getThumbnails().isEmpty()) {
			modelVideo.setThumbnail(videoEntry.getMediaGroup().getThumbnails().get(0).getUrl());

		}

		if (videoEntry.getMediaGroup().getDescription() != null) {
			modelVideo.setDescription(videoEntry.getMediaGroup().getDescription().getPlainTextContent());
		}

		if (videoEntry.getHtmlLink() != null) {

			String reprodVideoId = videoEntry.getHtmlLink().getHref().substring(31, 42);

			// modelVideoMinimal.setUrl(videoEntry.getHtmlLink().getHref());

			modelVideo.setUrl("http://www.youtube.com/embed/" + reprodVideoId);

			// modelVideo.setUrl(videoEntry.getHtmlLink().getHref());
		}

		if (videoEntry.getMediaGroup().getDuration() != null) {
			modelVideo.setDuration(videoEntry.getMediaGroup().getDuration());
		}

		if (videoEntry.getMediaGroup().getKeywords() != null) {
			modelVideo.setKeyWords(videoEntry.getMediaGroup().getKeywords().getKeywords());
		}

		if (videoEntry.getRating() != null) {
			modelVideo.setRating(videoEntry.getRating().getAverage());
		}

		modelVideo.setPublicationState(convertYouTubeStateToModelState(videoEntry.getMediaGroup().isPrivate()));

		modelVideo.setPublished(convertDateTimeToCalendar(videoEntry.getPublished()));

		return modelVideo;

	}

	/**
	 * converts a YouTube VideoEntry object into a model YouTubeVideoMinimal
	 * object
	 * 
	 * @return a model YouTubeVideoMinimal object
	 */
	private YouTubeVideoMinimal convertVideoEntryToModelVideoMinimal(
			VideoEntry videoEntry) {

		YouTubeVideoMinimal modelVideoMinimal = new YouTubeVideoMinimal();

		// sets the attributes
		modelVideoMinimal.setTitle(videoEntry.getTitle().getPlainText());

		String youTubeVideoId = getVideoId(videoEntry.getId());
		modelVideoMinimal.setVideoId(youTubeVideoId);

		if (!videoEntry.getMediaGroup().getThumbnails().isEmpty()) {
			modelVideoMinimal.setThumbnail(videoEntry.getMediaGroup().getThumbnails().get(0).getUrl());
		}

		if (videoEntry.getMediaGroup().getDescription() != null) {
			modelVideoMinimal.setDescription(videoEntry.getMediaGroup().getDescription().getPlainTextContent());
		}


		// sets the video url
		// if a video is removed but you do not remove it from your favorites,
		// the url is no longer available
		if (videoEntry.getHtmlLink() != null) {

			String reprodVideoId = videoEntry.getHtmlLink().getHref().substring(31, 42);

			// modelVideoMinimal.setUrl(videoEntry.getHtmlLink().getHref());

			modelVideoMinimal.setUrl("http://www.youtube.com/embed/"+ reprodVideoId);

		}

		// if the video has keywords..
		if (videoEntry.getMediaGroup() != null	&& videoEntry.getMediaGroup().getKeywords() != null) {
			if (!videoEntry.getMediaGroup().getKeywords().getKeywords().isEmpty()) {

				List<String> keywords = new ArrayList<String>();
				Iterator<String> videoKeywordIterator = videoEntry.getMediaGroup().getKeywords().getKeywords().iterator();

				while (videoKeywordIterator.hasNext()) {
					keywords.add(videoKeywordIterator.next());
				}

				modelVideoMinimal.setKeywords(keywords);
			}
		}

		return modelVideoMinimal;

	}

	/**
	 * converts a YouTube video list into a model minimal video list
	 * 
	 * @param videoEntryList
	 *            a YouTube VideoEntry object list
	 * @return a model YouTubeVideoMinimal object list
	 */

	private List<YouTubeVideoMinimal> convertVideoEntryListToModelVideoMinimalList(List<VideoEntry> videoEntryList) {

		List<YouTubeVideoMinimal> modelVideoMinimalList = new ArrayList<YouTubeVideoMinimal>();

		// gets the iterator from the VideoEntry object list
		Iterator<VideoEntry> videoEntryListIterator = videoEntryList.iterator();

		YouTubeVideoMinimal modelVideoMinimalToAdd = null;

		// loops through the VideoEntry list,converts each item into a model
		// YouTubeVideoMinimal object and adds them into a model
		// YouTubeVideoMinimal list
		while (videoEntryListIterator.hasNext()) {

			modelVideoMinimalToAdd = convertVideoEntryToModelVideoMinimal(videoEntryListIterator.next());
			modelVideoMinimalList.add(modelVideoMinimalToAdd);
		}

		return modelVideoMinimalList;

	}

	/**
	 * gets the YouTube Comments from a video
	 * 
	 * @param videoEntry
	 *            a VideoEntry object to get the comments
	 * @return a CommentEntry object list that contains the comments
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<CommentEntry> getYouTubeComments(VideoEntry videoEntry, Integer numElements) throws YouTubeException {

		List<CommentEntry> commentEntryList = new ArrayList<CommentEntry>();

		// if the videoEntry has comments...
		if (videoEntry.getComments() != null) {

			// gets the url for getting the comment feed
			String commentFeedUrl = videoEntry.getComments().getFeedLink()
					.getHref();

			// gets the comment feed
			CommentFeed commentFeed = null;
			commentFeed = getYouTubeCommentFeed(commentFeedUrl);

			// gets the first hundred entries from the feed
			commentEntryList = getCommentEntries(commentFeed, numElements);

		}
		return commentEntryList;

	}

	/**
	 * converts a YouTube CommentEntry list into a model Comment list
	 * 
	 * @return a model Comment list
	 */
	private List<YouTubeComment> convertCommentEntryListToModelCommentList(List<CommentEntry> commentEntryList) {

		List<YouTubeComment> modelCommentList = new ArrayList<YouTubeComment>();

		// gets the iterator from the CommentEntry object list
		Iterator<CommentEntry> commentEntryListIterator = commentEntryList.iterator();

		YouTubeComment modelCommentToAdd = null;

		// loops through the CommentEntry list,converts each item into a
		// YouTubeComment object and adds them to a list of model YouTubeComment
		// list
		while (commentEntryListIterator.hasNext()) {
			modelCommentToAdd = convertCommentEntryToModelComment(commentEntryListIterator.next());
			modelCommentList.add(modelCommentToAdd);
		}

		return modelCommentList;
	}

	/**
	 * converts a YouTube CommentEntry object into a model Comment object
	 * 
	 * @param commentEntry
	 *            a YouTube CommentEntry object
	 * @return a model Comment object
	 */
	private YouTubeComment convertCommentEntryToModelComment(CommentEntry commentEntry) {

		YouTubeComment modelComment = new YouTubeComment();

		// converts DateTime to Calendar and sets the published attribute
		Calendar modelPublished = convertDateTimeToCalendar(commentEntry.getPublished());
		modelComment.setPublished(modelPublished);

		// sets the rest of attributes
		modelComment.setAuthors(convertPersonsToStringAuthors(commentEntry.getAuthors()));
		modelComment.setText(commentEntry.getPlainTextContent());

		return modelComment;
	}

	/**
	 * converts DateTime objects into Calendar objects
	 * 
	 * @param dateTime
	 *            a DateTime object to convert
	 * @return a Calendar object
	 */
	private Calendar convertDateTimeToCalendar(DateTime dateTime) {
		Calendar modelCalendar = Calendar.getInstance();
		modelCalendar.setTimeInMillis(dateTime.getValue());

		return modelCalendar;
	}

	/**
	 * converts a YouTube State boolean object into a model State object
	 * 
	 * @param isPrivate
	 *            a YouTube State boolean object
	 * @return a model State object
	 */
	private State convertYouTubeStateToModelState(boolean isPrivate) {

		State modelState = null;
		if (isPrivate) {
			modelState = State.PRIVATE;
		} else {
			modelState = State.PUBLIC;
		}

		return modelState;
	}

	/**
	 * converts a model OrderBy object into a YouTube OrderBy object
	 * 
	 * @param modelOrder
	 *            a readable String that represents a model OrderBy object
	 * @return a YouTube OrderBy object
	 */
	private com.google.gdata.client.youtube.YouTubeQuery.OrderBy convertModelOrderToYouTubeOrder(String modelOrder) {
		com.google.gdata.client.youtube.YouTubeQuery.OrderBy youTubeOrder = null;

		if (modelOrder.equals("Visitas")) {
			youTubeOrder = com.google.gdata.client.youtube.YouTubeQuery.OrderBy.VIEW_COUNT;

		} else if (modelOrder.equals("Relevancia")) {
			youTubeOrder = com.google.gdata.client.youtube.YouTubeQuery.OrderBy.RELEVANCE;

		} else if (modelOrder.equals("Puntuación")) {
			youTubeOrder = com.google.gdata.client.youtube.YouTubeQuery.OrderBy.RATING;

		} else if (modelOrder.equals("Fecha")) {
			youTubeOrder = com.google.gdata.client.youtube.YouTubeQuery.OrderBy.PUBLISHED;

		}

		return youTubeOrder;
	}

	/**
	 * converts a YouTube playlist into a model playlist
	 * 
	 * @param youTubePlayList
	 *            a YouTube PlaylistLinkEntry object
	 * @return a model YouTubePlayList object
	 */
	private YouTubePlayList convertPlayListToModelPlayList(PlaylistLinkEntry youTubePlayList) {

		YouTubePlayList modelPlaylist = new YouTubePlayList();

		// sets the attributes
		modelPlaylist.setTitle(youTubePlayList.getTitle().getPlainText());
		modelPlaylist.setTotalResults(youTubePlayList.getCountHint());
		modelPlaylist.setPlaylistId(youTubePlayList.getPlaylistId());
		modelPlaylist.setUrl(youTubePlayList.getHtmlLink().getHref());
		modelPlaylist.setAuthors(convertPersonsToStringAuthors(youTubePlayList.getAuthors()));
		modelPlaylist.setDescription(youTubePlayList.getSummary().getPlainText());

		return modelPlaylist;
	}

	/**
	 * converts a YouTube playlist entry into a model playlist entry
	 * 
	 * @param playListEntry
	 *            a YouTube PlaylistEntry object (extends from VideoEntry)
	 * @return a model YouTubePlayListEntry object (extends from model
	 *         YouTubeVideoMinimal)
	 */

	private YouTubePlayListEntry convertPlayListEntryToModelPlayListEntry(PlaylistEntry playListEntry) {

		YouTubePlayListEntry modelPlayListEntry;
		YouTubeVideoMinimal videoMinimalToConvert;

		// does the conversion using the method for VideoEntry because
		// PlaylistEntry extends from that
		videoMinimalToConvert = convertVideoEntryToModelVideoMinimal(playListEntry);

		// creates the model YouTubePlayListEntry object from a model
		// YouTubeVideoMinimal object
		modelPlayListEntry = new YouTubePlayListEntry(videoMinimalToConvert);

		// sets the position of the entry in the playlist
		modelPlayListEntry.setPosition(playListEntry.getPosition());

		return modelPlayListEntry;

	}

	/**
	 * converts a list of YouTube playlist entries into a list of model playlist
	 * entries
	 * 
	 * @param playListEntries
	 *            a PlaylistEntry object list
	 * @return a model YouTubePlayListEntry object list
	 */
	private List<YouTubePlayListEntry> convertListOfPlayListEntryToListOfModelPlayListEntry(List<PlaylistEntry> playListEntries) {

		List<YouTubePlayListEntry> modelPlayListEntries = new ArrayList<YouTubePlayListEntry>();

		// gets the iterator from the PlaylistEntry object list
		Iterator<PlaylistEntry> PlayListEntryListIterator = playListEntries.iterator();

		PlaylistEntry currentPlaylistEntry = null;
		YouTubePlayListEntry modelPlayLisEntryToAdd = null;

		// loops through the PlaylistEntry list, converts each item into a
		// YouTubePlayListEntry object and adds them to a list of
		// YouTubePlayListEntry objects
		while (PlayListEntryListIterator.hasNext()) {

			currentPlaylistEntry = PlayListEntryListIterator.next();
			modelPlayLisEntryToAdd = convertPlayListEntryToModelPlayListEntry(currentPlaylistEntry);
			modelPlayListEntries.add(modelPlayLisEntryToAdd);
		}

		return modelPlayListEntries;
	}

	/**
	 * converts a list of YouTube playlists into a list of model playlists
	 * 
	 * @param youTubePlaylists
	 *            a list of YouTube PlaylistLinkEntry objects
	 * @return a list of model YouTubePlayList objects
	 */
	private List<YouTubePlayList> convertListOfPlayListsToListOfModelPlayLists(List<PlaylistLinkEntry> youTubePlaylists) {

		List<YouTubePlayList> modelListOfPlayLists = new ArrayList<YouTubePlayList>();

		// gets the iterator from the PlaylistLinkEntry object list
		Iterator<PlaylistLinkEntry> ListOfPlayListsIterator = youTubePlaylists.iterator();

		YouTubePlayList currentPlayList = null;

		// loops through the PlaylistLinkEntry list, converts each item into a
		// YouTubePlayList and adds them to a list of YouTubePlayList objects
		while (ListOfPlayListsIterator.hasNext()) {
			currentPlayList = convertPlayListToModelPlayList(ListOfPlayListsIterator.next());
			modelListOfPlayLists.add(currentPlayList);

		}
		return modelListOfPlayLists;
	}

	/**
	 * searches videos using diferent parameters
	 * 
	 * @param order
	 *            search order (view count, rating, relevance, published)
	 * @param category
	 *            search category. Set to null if not want to use
	 * @param keywords
	 *            keywords assigned to the video to search
	 * @return a model YouTubeVideo object list
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<YouTubeVideoMinimal> searchVideos(OrderBy order,Category category, List<String> keywords) throws YouTubeException {

		List<YouTubeVideoMinimal> foundModelVideoList = null;
		YouTubeQuery query;

		try {
			// constructs the YouTubeQuery object for searching videos
			query = new YouTubeQuery(new URL(VIDEOS_FEED));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// converts the model Order into YouTube order
		com.google.gdata.client.youtube.YouTubeQuery.OrderBy youTubeOrder;
		youTubeOrder = convertModelOrderToYouTubeOrder(order.getReadableNameOrderBy());
		// sets the YouTube Order
		query.setOrderBy(youTubeOrder);

		// constructs a query String for a multiple keyword search
		Iterator<String> keyWordsIterator = keywords.iterator();
		String keyWordsToSearch = "";
		while (keyWordsIterator.hasNext()) {
			String currentKeyWord = keyWordsIterator.next();
			keyWordsToSearch = keyWordsToSearch + " " + currentKeyWord;
		}

		// sets the keyword list for the search
		query.setFullTextQuery(keyWordsToSearch);

		// sets the search as 'strict' to get no restricted content
		query.setSafeSearch(SafeSearch.STRICT);

		// restricts the videos for this country
		// String countryCode="ES";
		String countryCode = Locale.getDefault().getCountry();
		query.setCountryRestriction(countryCode);

		// if the search uses category parameter...
		if (category != null) {

			// converts the model Category into YouTube Category
			com.google.gdata.data.Category youTubeCategory = null;
			youTubeCategory = convertModelCategoryToYouTubeCategory(category);

			// constructs a query filter for a search by category to restrict
			// the videos to find in the selected category
			Query.CategoryFilter categoryFilter = new Query.CategoryFilter();
			com.google.gdata.data.Category categoryToSearch;
			categoryToSearch = new com.google.gdata.data.Category(YouTubeNamespace.CATEGORY_SCHEME, youTubeCategory.getTerm());
			categoryFilter.addCategory(categoryToSearch);
			query.addCategoryFilter(categoryFilter);
		}

		try {

			// service.setUserCredentials("davidjgc1", "davidyoutube");

			// does the search
			VideoFeed videoFeed = service.query(query, VideoFeed.class);

			// gets the list of the first hundred videos of the feed
			List<VideoEntry> videoEntryList = getVideoEntries(videoFeed, 100);

			// converts the found VideoEntry list into a model
			// YouTubeVideoMinimal list
			foundModelVideoList = convertVideoEntryListToModelVideoMinimalList(videoEntryList);

			// sets the category to false to initialize the category state
			// setCategoryDeprecated(false);

		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return foundModelVideoList;

	}

	/**
	 * 
	 * searches playlists using parameters
	 * 
	 * @param keywords
	 *            keywords assigned to the playlist to search
	 * @return a model YouTubePlayList object list
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<YouTubePlayList> searchPlayLists(List<String> keywords) throws YouTubeException {

		List<PlaylistLinkEntry> foundPlayLists = new ArrayList<PlaylistLinkEntry>();
		List<YouTubePlayList> foundModelPlayList = new ArrayList<YouTubePlayList>();
		YouTubeQuery query;

		try {
			// constructs the YouTubeQuery object for searching playlists
			query = new YouTubeQuery(new URL(PLAYLIST_FEED_PREFIX + "snippets"));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// orders results by relevance. The only way for playlists
		query.setOrderBy(YouTubeQuery.OrderBy.RELEVANCE);

		// constructs a query String for a multiple keyword search
		Iterator<String> keywordsIterator = keywords.iterator();
		String keywordsToSearch = "";
		while (keywordsIterator.hasNext()) {
			keywordsToSearch = keywordsToSearch + " " + keywordsIterator.next();
		}

		// sets the keyword list for the search
		query.setFullTextQuery(keywordsToSearch);

		try {

			// does the search
			PlaylistLinkFeed playlistLinkFeed = service.query(query, PlaylistLinkFeed.class);

			// gets the list of playlists found
			foundPlayLists = playlistLinkFeed.getEntries();

			logger.debug("listas encontradas " + foundPlayLists.size());

			// gets the list of the first hundred playlists of the feed
			foundPlayLists = getPlayListLinkEntries(playlistLinkFeed, 100);

			// converts the list of playlists into a model list of playlists
			foundModelPlayList = convertListOfPlayListsToListOfModelPlayLists(foundPlayLists);

		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return foundModelPlayList;
	}	

	@Override
	public void changeUser(Integer userId, String userName, String password) throws YouTubeException, YouTubeAuthException {
		logger.debug("changeUser..");

		// gets the information about user from the database
		YoutubeUser youtubeUserStored = getYoutubeUser(userId);

		// if the user has already been stored ..
		if (youtubeUserStored != null) {
			logger.debug("the user has already been registered on the service");

			// if the username and pass to modify are the than the old data
			if (youtubeUserStored.getUsername().equals(userName) && youtubeUserStored.getPass().equals(password)) {
				throw new YouTubeException("the username and pass from user to modify are the same than the old data");
			}

			// sets the credentials of the new YouTube user
			EntityManager em = youTubeDB.getEntityManager();

			try {
				checkUserCredentials(userName, password);

				// gets the user with that id for updating the record with the username and pass
				em.getTransaction().begin();
				YoutubeUser youtubeUserToChange = youTubeDB.selectYouTubeUserQuery(em, userId);
				youtubeUserToChange.setPass(password);
				youtubeUserToChange.setUsername(userName);
				em.merge(youtubeUserToChange);
				em.getTransaction().commit();
				youTubeDB.closeEntityManager(em);
				logger.debug("username or password from user " + youtubeUserToChange.getUserid() + " updated");

			} catch (YouTubeAuthException e) {
				youTubeDB.closeEntityManager(em);
				throw new YouTubeAuthException("user modification fails. "+ e.getMessage()+ ",you need a correct username or password", e.getCause());	

			} catch (Exception e) {
				em.getTransaction().rollback();
				youTubeDB.closeEntityManager(em);
				throw new YouTubeException("the user could not be modified: " + e.getMessage());
			}		

			logger.debug("user modification ends");

			// if the user has not been stored yet..
		} else {
			throw new YouTubeException("user has not been registered yet");
		}

	}

	@Override
	public void deleteUser(Integer userId) throws YouTubeException, YouTubeAuthException {
		logger.debug("deleteUser..");

		// gets the information about user from the database
		YoutubeUser youtubeUserStored = getYoutubeUser(userId);

		// if the user has already been stored ..
		if (youtubeUserStored != null) {

			// gets the user with that id for deleting the record
			EntityManager em = youTubeDB.getEntityManager();
			em.getTransaction().begin();
			YoutubeUser youtubeUserToDelete = youTubeDB.selectYouTubeUserQuery(em, userId);
			em.remove(youtubeUserToDelete);

			// gets the keyword preferences of user to delete..
			List<Userkeyword> youtubeUserKeywords = youTubeDB.selectUserKeywordQuery(em, userId);

			// if user has preferences..
			if (youtubeUserKeywords != null && !youtubeUserKeywords.isEmpty()) {
				// loops through the prefrence list..
				Iterator<Userkeyword> youtubeUserKeywordsIterator = youtubeUserKeywords.iterator();
				Userkeyword currentYoutubeUserKeyword = null;

				// and removes each keyword preference
				while (youtubeUserKeywordsIterator.hasNext()) {
					currentYoutubeUserKeyword = youtubeUserKeywordsIterator.next();
					em.remove(currentYoutubeUserKeyword);
				}
			}

			try {
				em.getTransaction().commit();
				logger.debug("user " + youtubeUserToDelete.getUserid()+ " removed");

			} catch (Exception e) {
				em.getTransaction().rollback();
				youTubeDB.closeEntityManager(em);
				throw new YouTubeException("the user could not be deleted: "+ e.getMessage());
			}

			youTubeDB.closeEntityManager(em);

		} else {
			throw new YouTubeException("user has not been registered yet");
		}

	}

	@Override
	public YouTubeResult search(YouTubeType type, OrderBy order, List<String> keywords, Integer indexStart, Integer maxResults) throws YouTubeException {

		logger.debug("search..");

		// sets the anonymous user
		resetYouTubeService();

		YouTubeResult modelSearchResult = new YouTubeResult();

		// the offset to get the elements must be greater than 0
		if (indexStart < 1) {
			indexStart = 1;
		}

		if (type.getReadableNameType().equals("Videos")) {

			// searches videos and adds them to the YouTubeResult object
			List<YouTubeVideoMinimal> foundVideoList = null;
			foundVideoList = searchVideos(order, null, keywords, indexStart, maxResults);
			modelSearchResult.setVideos(foundVideoList);

		} else if (type.getReadableNameType().equals("Listas reproduccion")) {

			// searches playlists and adds them to the YouTubeResult object
			List<YouTubePlayList> foundPlayListList = null;
			foundPlayListList = searchPlayLists(keywords, indexStart, maxResults);
			modelSearchResult.setPlayLists(foundPlayListList);

			// search all...
		} else {

			// searches videos and adds them to the YouTubeResult object
			List<YouTubeVideoMinimal> foundVideoList = null;
			foundVideoList = searchVideos(order, null, keywords, indexStart, maxResults);
			modelSearchResult.setVideos(foundVideoList);

			// searches playlists and adds them to the YouTubeResult object
			List<YouTubePlayList> foundPlayListList = null;
			foundPlayListList = searchPlayLists(keywords, indexStart,maxResults);
			modelSearchResult.setPlayLists(foundPlayListList);
		}

		return modelSearchResult;

	}

	/**
	 * 
	 * searches playlists using parameters and paging
	 * 
	 * @param keywords
	 *            keywords assigned to the playlist to search
	 * @param indexStart
	 *            offset to get the elements
	 * @param maxResults
	 *            max number of elements to return
	 * @return a model YouTubePlayList object list
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */

	private List<YouTubePlayList> searchPlayLists(List<String> keywords, Integer indexStart, Integer maxResults) throws YouTubeException {

		List<PlaylistLinkEntry> foundPlayLists = new ArrayList<PlaylistLinkEntry>();
		List<YouTubePlayList> foundModelPlayList = new ArrayList<YouTubePlayList>();
		YouTubeQuery query;

		try {
			// constructs the YouTubeQuery object for searching playlists
			query = new YouTubeQuery(new URL(PLAYLIST_FEED_PREFIX + "snippets"));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// orders results by relevance. The only way for playlists
		query.setOrderBy(YouTubeQuery.OrderBy.RELEVANCE);

		// sets the max number of elements to return
		query.setMaxResults(maxResults);
		// sets the offset to get the elements
		query.setStartIndex(indexStart);

		// constructs a query String for a multiple keyword search
		Iterator<String> keywordsIterator = keywords.iterator();
		String keywordsToSearch = "";
		while (keywordsIterator.hasNext()) {
			keywordsToSearch = keywordsToSearch + " " + keywordsIterator.next();
		}

		// sets the keyword list for the search
		query.setFullTextQuery(keywordsToSearch);

		try {

			// does the search
			PlaylistLinkFeed playlistLinkFeed = service.query(query, PlaylistLinkFeed.class);

			// gets the list of playlists found
			foundPlayLists = playlistLinkFeed.getEntries();

			// gets the playlist list of the feed
			foundPlayLists = getPlayListLinkEntries(playlistLinkFeed,
					maxResults);

			// converts the list of playlists into a model list of playlists
			foundModelPlayList = convertListOfPlayListsToListOfModelPlayLists(foundPlayLists);

		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return foundModelPlayList;
	}

	/**
	 * searches videos using diferent parameters and paging
	 * 
	 * @param order
	 *            search order (view count, rating, relevance, published)
	 * @param category
	 *            search category. Set to null if not want to use
	 * @param keywords
	 *            keywords assigned to the video to search
	 * @param indexStart
	 *            offset to get the elements
	 * @param maxResults
	 *            max number of elements to return
	 * @return a model YouTubeVideo object list
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */
	private List<YouTubeVideoMinimal> searchVideos(OrderBy order, Category category, List<String> keywords, Integer indexStart,	Integer maxResults) throws YouTubeException {

		List<YouTubeVideoMinimal> foundModelVideoList = null;
		YouTubeQuery query;

		try {
			// constructs the YouTubeQuery object for searching videos
			query = new YouTubeQuery(new URL(VIDEOS_FEED));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// converts the model Order into YouTube order
		com.google.gdata.client.youtube.YouTubeQuery.OrderBy youTubeOrder;
		youTubeOrder = convertModelOrderToYouTubeOrder(order.getReadableNameOrderBy());
		// sets the YouTube Order
		query.setOrderBy(youTubeOrder);

		// sets the max number of elements to return
		query.setMaxResults(maxResults);
		// sets the offset to get the elements
		query.setStartIndex(indexStart);

		// constructs a query String for a multiple keyword search
		Iterator<String> keyWordsIterator = keywords.iterator();
		String keyWordsToSearch = "";
		while (keyWordsIterator.hasNext()) {
			String currentKeyWord = keyWordsIterator.next();
			keyWordsToSearch = keyWordsToSearch + " " + currentKeyWord;
		}

		// sets the keyword list for the search
		query.setFullTextQuery(keyWordsToSearch);

		// sets the search as 'strict' to get no restricted content
		query.setSafeSearch(SafeSearch.STRICT);

		// restricts the videos for this country
		// String countryCode="ES";
		String countryCode = Locale.getDefault().getCountry();
		query.setCountryRestriction(countryCode);

		// if the search uses category parameter...
		if (category != null) {

			// converts the model Category into YouTube Category
			com.google.gdata.data.Category youTubeCategory = null;
			youTubeCategory = convertModelCategoryToYouTubeCategory(category);

			// constructs a query filter for a search by category to restrict
			// the videos to find in the selected category
			Query.CategoryFilter categoryFilter = new Query.CategoryFilter();
			com.google.gdata.data.Category categoryToSearch;
			categoryToSearch = new com.google.gdata.data.Category(YouTubeNamespace.CATEGORY_SCHEME, youTubeCategory.getTerm());
			categoryFilter.addCategory(categoryToSearch);
			query.addCategoryFilter(categoryFilter);
		}

		try {

			// service.setUserCredentials("davidjgc1", "davidyoutube");

			// does the search
			VideoFeed videoFeed = service.query(query, VideoFeed.class);

			// gets the video list of the feed
			List<VideoEntry> videoEntryList = getVideoEntries(videoFeed, maxResults);

			// converts the found VideoEntry list into a model
			// YouTubeVideoMinimal list
			foundModelVideoList = convertVideoEntryListToModelVideoMinimalList(videoEntryList);

			// sets the category to false to initialize the category state
			// setCategoryDeprecated(false);

		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		return foundModelVideoList;

	}

	@Override
	public List<YouTubeVideoMinimal> explore(Category category, OrderBy order,
			List<String> keyWords, Integer indexStart, Integer maxResults) throws YouTubeException {

		logger.debug("explore..");

		// sets the anonymous user
		resetYouTubeService();

		List<YouTubeVideoMinimal> foundVideoList = null;

		if (category.isCategoryDeprecated()) {
			logger.debug("this category is deprecated");
		}

		// searches videos using a category
		foundVideoList = searchVideos(order, category, keyWords, indexStart, maxResults);

		// sets the category to false to initialize the category state
		// setCategoryDeprecated(false);

		return foundVideoList;

	}

	@Override
	public List<YouTubeVideoMinimal> getUserVideos(YouTubeUser user, Integer indexStart, Integer maxResults) throws YouTubeException, YouTubeAuthException {

		logger.debug("getUserVideos..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubeVideoMinimal> modelUploadVideoList = null;

		// url for getting the upload videos by the current user
		String userVideoFeedUrl = USER_FEED_PREFIX + "default/uploads";

		VideoFeed userVideoFeed = null;
		YouTubeQuery query = null;

		try {
			// constructs the YouTubeQuery object for getting the uploaded
			// videos by
			// paging
			query = new YouTubeQuery(new URL(userVideoFeedUrl));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// sets the offset and max number of elements to get
		query.setMaxResults(maxResults);
		query.setStartIndex(indexStart);

		// gets the video feed with the uploaded videos
		try {
			userVideoFeed = service.query(query, VideoFeed.class);
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// gets the video list from the feed
		List<VideoEntry> videoEntryList = null;
		videoEntryList = getVideoEntries(userVideoFeed, maxResults);

		// transforms the video entry list into a model video minimal list
		modelUploadVideoList = convertVideoEntryListToModelVideoMinimalList(videoEntryList);

		return modelUploadVideoList;

	}

	@Override
	public void updateVideo(YouTubeUser user, String youtubeVideoId, String title, String description, List<String> keyWords)
			throws YouTubeException, YouTubeAuthException {

		logger.debug("updateVideo..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		// gets the video from the url
		VideoEntry videoEntry = getVideoEntryFromYoutubeVideoId(youtubeVideoId);
		String videoId = videoEntry.getMediaGroup().getVideoId();

		// gets the video to modify
		String videoEntryUrl = USER_FEED_PREFIX + "default/uploads/" + videoId;
		VideoEntry videoEntryToModify = getYouTubeVideoEntry(videoEntryUrl);

		if (videoEntryToModify.getEditLink() != null) {
			logger.debug("Video is editable by current user.");
		} else {
			logger.debug(videoEntryToModify.getEditLink().toString());
			throw new YouTubeException("Video is not editable by current user");
		}

		// sets the new title
		if (title != null && videoEntryToModify.getMediaGroup() != null
				&& videoEntryToModify.getMediaGroup().getTitle() != null) {
			videoEntryToModify.getMediaGroup().getTitle().setPlainTextContent(
					title);
		}

		// sets the new description
		if (description != null && videoEntryToModify.getMediaGroup() != null
				&& videoEntryToModify.getMediaGroup().getDescription() != null) {
			videoEntryToModify.getMediaGroup().getDescription()
			.setPlainTextContent(description);
		}

		// sets the new keywords
		if (keyWords != null && !keyWords.isEmpty()
				&& videoEntryToModify.getMediaGroup() != null
				&& videoEntryToModify.getMediaGroup().getKeywords() != null) {
			videoEntryToModify.getMediaGroup().getKeywords().clearKeywords();
			videoEntryToModify.getMediaGroup().getKeywords().addKeywords(
					keyWords);
		}

		// updates the video entry
		try {
			videoEntryToModify.update();
		} catch (IOException e) {
			throw new YouTubeException("Could not update the video: "
					+ e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException("Could not update the video: "
					+ e.getMessage(), e.getCause());
		}

	}

	@Override
	public List<YouTubeVideoMinimal> getFavorites(YouTubeUser user,	Integer indexStart, Integer maxResults)
			throws YouTubeException, YouTubeAuthException {

		logger.debug("getFavorites..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubeVideoMinimal> modelFavoriteVideoList = null;

		// url for getting favorites from the current user
		String favoriteUrl = USER_FEED_PREFIX + "default/favorites";

		// // gets the video feed with the favorite videos
		// VideoFeed favoriteVideoFeed = null;
		// favoriteVideoFeed = getYouTubeVideoFeed(favoriteUrl);

		VideoFeed favoriteVideoFeed = null;
		YouTubeQuery query = null;

		try {
			// constructs the YouTubeQuery object for getting the favorite
			// videos by paging
			query = new YouTubeQuery(new URL(favoriteUrl));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// sets the offset and max number of elements to get
		query.setMaxResults(maxResults);
		query.setStartIndex(indexStart);

		// gets the video feed with the favorite videos
		try {
			favoriteVideoFeed = service.query(query, VideoFeed.class);
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// gets the favorite video list from the feed
		List<VideoEntry> favoriteVideoList = null;
		favoriteVideoList = getVideoEntries(favoriteVideoFeed, maxResults);

		// converts the list of video entries into a model YouTubeVideoMinimal
		// object list
		modelFavoriteVideoList = convertVideoEntryListToModelVideoMinimalList(favoriteVideoList);

		return modelFavoriteVideoList;
	}

	@Override
	public List<YouTubePlayList> getPlayLists(YouTubeUser user,
			Integer indexStart, Integer maxResults)
					throws YouTubeException, YouTubeAuthException {

		logger.debug("getPlayLists..");

		// checks if user exists and sets the credentials
		checkUserCredentials(user.getUserName(), user.getPassword());

		List<YouTubePlayList> userListOfModelPlaylists = new ArrayList<YouTubePlayList>();

		// url for getting the playlists from the current user
		String playListFeedUrl = USER_FEED_PREFIX + "default/playlists";

		// gets the feed with the playlists..
		PlaylistLinkFeed playlistLinkFeed = null;
		YouTubeQuery query = null;

		try {
			// constructs the YouTubeQuery object for getting the playlist feed
			// by paging
			query = new YouTubeQuery(new URL(playListFeedUrl));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// sets the offset and max number of elements to get
		query.setMaxResults(maxResults);
		query.setStartIndex(indexStart);

		// gets the playlist feed with the playlists of the user
		try {
			playlistLinkFeed = service.query(query, PlaylistLinkFeed.class);
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// gets the list with the playlists from the feed
		List<PlaylistLinkEntry> listOfPlayListLinkEntries = null;
		listOfPlayListLinkEntries = getPlayListLinkEntries(playlistLinkFeed,
				maxResults);

		// if the list is not empty...
		if (!listOfPlayListLinkEntries.isEmpty()) {

			// converts the YouTube list of playlists into a model list of
			// playlists
			userListOfModelPlaylists = convertListOfPlayListsToListOfModelPlayLists(listOfPlayListLinkEntries);

		}

		return userListOfModelPlaylists;

	}

	@Override
	public StandarVideos getStandarVideos(Integer indexStart, Integer maxResults) throws YouTubeException {

		logger.debug("getStandarVideos..");

		// sets the anonymous user
		resetYouTubeService();

		StandarVideos modelStandarVideos = new StandarVideos();

		List<YouTubeVideoMinimal> modelMostViewedVideoList = null;
		List<YouTubeVideoMinimal> modelRecentVideoList = null;

		// gets the most viewed video list with the first hundred elements and
		// adds them to the StandarVideos object
		modelMostViewedVideoList = getModelMostViewedVideos(indexStart,
				maxResults);
		modelStandarVideos.setMostViewed(modelMostViewedVideoList);

		// gets the most recent video list with the first hundred elements and
		// adds them to the StandarVideos object
		modelRecentVideoList = getModelRecentVideos(indexStart, maxResults);
		modelStandarVideos.setRecent(modelRecentVideoList);

		return modelStandarVideos;

	}

	/**
	 * gets a model most recent video list by paging
	 * 
	 * @param indexStart
	 *            offset to get the elements. Must be greater than 0
	 * @param maxResults
	 *            max number of elements to return
	 * @return a model YouTubeVideoMinimal object list that contains the videos
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */

	private List<YouTubeVideoMinimal> getModelRecentVideos(Integer indexStart,
			Integer maxResults) throws YouTubeException {

		List<YouTubeVideoMinimal> modelRecentVideoList = new ArrayList<YouTubeVideoMinimal>();

		// url for getting most recent video feed
		String mostRecentUrl = STANDARD_FEED + "most_recent";

		// gets the most recent video feed
		VideoFeed mostRecentFeed = null;

		YouTubeQuery query = null;
		try {
			// constructs the YouTubeQuery object for getting the most recent
			// videos by paging
			query = new YouTubeQuery(new URL(mostRecentUrl));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// sets the offset and max number of elements to get
		query.setMaxResults(maxResults);
		query.setStartIndex(indexStart);

		// gets the video feed with the most recent videos
		try {
			mostRecentFeed = service.query(query, VideoFeed.class);
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// gets the video list from the feed
		List<VideoEntry> mostRecentVideoList = null;
		mostRecentVideoList = getVideoEntries(mostRecentFeed, maxResults);

		// converts the YouTube video list into a model minimal video list
		modelRecentVideoList = convertVideoEntryListToModelVideoMinimalList(mostRecentVideoList);

		return modelRecentVideoList;

	}

	/**
	 * gets a model most viewed video list by paging
	 * 
	 * @param indexStart
	 *            offset to get the elements. Must be greater than 0
	 * @param maxResults
	 *            max number of elements to return
	 * @return a model YouTubeVideoMinimal object list that contains the videos
	 * @throws YouTubeException
	 *             If any error in the service appears
	 */

	private List<YouTubeVideoMinimal> getModelMostViewedVideos(
			Integer indexStart, Integer maxResults) throws YouTubeException {

		List<YouTubeVideoMinimal> modelMostViewedVideoList = new ArrayList<YouTubeVideoMinimal>();

		// url for getting most viewed video feed
		String mostViewedUrl = STANDARD_FEED + "most_viewed"; // ES/most_viewed?time=all_time

		// gets the most viewed video feed
		VideoFeed mostViewedFeed = null;

		YouTubeQuery query = null;
		try {
			// constructs the YouTubeQuery object for getting the most viewed
			// videos by paging
			query = new YouTubeQuery(new URL(mostViewedUrl));
		} catch (MalformedURLException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// sets the offset and max number of elements to get
		query.setMaxResults(maxResults);
		query.setStartIndex(indexStart);

		// gets the video feed with the most viewed videos
		try {
			mostViewedFeed = service.query(query, VideoFeed.class);
		} catch (IOException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		} catch (ServiceException e) {
			throw new YouTubeException(e.getMessage(), e.getCause());
		}

		// gets the video list from the feed
		List<VideoEntry> mostViewedVideoList = null;
		mostViewedVideoList = getVideoEntries(mostViewedFeed, maxResults);

		// converts the YouTube video list into a model minimal video list
		modelMostViewedVideoList = convertVideoEntryListToModelVideoMinimalList(mostViewedVideoList);

		return modelMostViewedVideoList;

	}

	@Override
	public List<YouTubeVideoMinimal> filterList(String filter,
			List<YouTubeVideoMinimal> videoList) {

		logger.debug("filterList..");

		// sets the anonymous user
		resetYouTubeService();

		List<YouTubeVideoMinimal> filteredVideoList = new ArrayList<YouTubeVideoMinimal>();
		boolean matchVideo = false;

		// if video list has elements..
		if (videoList != null && !videoList.isEmpty()) {

			Iterator<YouTubeVideoMinimal> videoListIterator = videoList
					.iterator();

			String currentDescription = null;
			String currentTitle = null;
			String currentKeyword = null;

			// loops through the video list..
			while (videoListIterator.hasNext()) {

				// gets the title and description of the current video..
				YouTubeVideoMinimal currentVideo = videoListIterator.next();
				currentDescription = currentVideo.getDescription();
				currentTitle = currentVideo.getTitle();

				// if the description of the video is not null
				if (currentDescription != null) {
					// gets the tokenizer from the description
					StringTokenizer descriptionTokenizer = new StringTokenizer(
							currentDescription);

					// if any word of the description matches with the filter
					// sets the flag to true
					while (descriptionTokenizer.hasMoreTokens()) {

						String currentDescriptionToken = (String) descriptionTokenizer
								.nextElement();

						if (currentDescriptionToken.compareToIgnoreCase(filter) == 0) {
							matchVideo = true;
						}

					}

				}

				// if the title of the video is not null
				if (currentTitle != null) {
					// gets the tokenizer from the title
					StringTokenizer titleTokenizer = new StringTokenizer(
							currentTitle);

					// if any word of the title matches with the filter sets
					// the flag to true
					while (titleTokenizer.hasMoreTokens()) {

						String currentTitleToken = (String) titleTokenizer
								.nextElement();

						if (currentTitleToken.compareToIgnoreCase(filter) == 0) {
							matchVideo = true;
						}

					}
				}

				// if the current video has keywords..
				if (currentVideo.getKeywords() != null
						&& !currentVideo.getKeywords().isEmpty()) {

					Iterator<String> keywordsIterator = currentVideo
							.getKeywords().iterator();

					while (keywordsIterator.hasNext()) {

						currentKeyword = keywordsIterator.next();

						// if the current keyword matches with the filter sets
						// the flag to true
						if (currentKeyword.compareToIgnoreCase(filter) == 0) {
							matchVideo = true;
						}

					}
				}

				// if the flag is true, the current video will be added to the
				// filtered list
				if (matchVideo) {
					filteredVideoList.add(currentVideo);
				}

				// initializes the flag for the next iteration..
				matchVideo = false;

			}

		}

		return filteredVideoList;
	}
}
